From 838ddd7e8a6db0beafca13f300b50c75faf8066d Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Fri, 23 Feb 2018 10:13:03 -0800 Subject: [PATCH 01/84] 623 fix --- .../sqlserver/jdbc/SQLServerConnection.java | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index c8232d71a..dcaa9234f 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -3620,34 +3620,36 @@ final void processEnvChange(TDSReader tdsReader) throws SQLServerException { } // Check if the hostNameInCertificate needs to be updated to handle the rerouted subdomain in Azure - String currentHostName = activeConnectionProperties.getProperty("hostNameInCertificate"); - if (null != currentHostName && currentHostName.startsWith("*") - && (null != routingServerName) /* skip the check for hostNameInCertificate if routingServerName is null */ - && routingServerName.indexOf('.') != -1) { - char[] currentHostNameCharArray = currentHostName.toCharArray(); - char[] routingServerNameCharArray = routingServerName.toCharArray(); - boolean hostNameNeedsUpdate = true; - - /* - * Check if routingServerName and hostNameInCertificate are from same domain by verifying each character in currentHostName from - * last until it reaches the character before the wildcard symbol (i.e. currentHostNameCharArray[1]) - */ - for (int i = currentHostName.length() - 1, j = routingServerName.length() - 1; i > 0 && j > 0; i--, j--) { - if (routingServerNameCharArray[j] != currentHostNameCharArray[i]) { - hostNameNeedsUpdate = false; - break; - } - } - - if (hostNameNeedsUpdate) { - String newHostName = "*" + routingServerName.substring(routingServerName.indexOf('.')); - activeConnectionProperties.setProperty("hostNameInCertificate", newHostName); - - if (connectionlogger.isLoggable(Level.FINER)) { - connectionlogger.finer(toString() + "Using new host to validate the SSL certificate"); - } - } - } + // Do not modify the hostNameInCertificate connection property, since we need this to match the server name + // of Azure SQL Server, which is in the form of .database.windows.net +// String currentHostName = activeConnectionProperties.getProperty("hostNameInCertificate"); +// if (null != currentHostName && currentHostName.startsWith("*") +// && (null != routingServerName) /* skip the check for hostNameInCertificate if routingServerName is null */ +// && routingServerName.indexOf('.') != -1) { +// char[] currentHostNameCharArray = currentHostName.toCharArray(); +// char[] routingServerNameCharArray = routingServerName.toCharArray(); +// boolean hostNameNeedsUpdate = true; +// +// /* +// * Check if routingServerName and hostNameInCertificate are from same domain by verifying each character in currentHostName from +// * last until it reaches the character before the wildcard symbol (i.e. currentHostNameCharArray[1]) +// */ +// for (int i = currentHostName.length() - 1, j = routingServerName.length() - 1; i > 0 && j > 0; i--, j--) { +// if (routingServerNameCharArray[j] != currentHostNameCharArray[i]) { +// hostNameNeedsUpdate = false; +// break; +// } +// } +// +// if (hostNameNeedsUpdate) { +// String newHostName = "*" + routingServerName.substring(routingServerName.indexOf('.')); +// activeConnectionProperties.setProperty("hostNameInCertificate", newHostName); +// +// if (connectionlogger.isLoggable(Level.FINER)) { +// connectionlogger.finer(toString() + "Using new host to validate the SSL certificate"); +// } +// } +// } isRoutedInCurrentAttempt = true; routingInfo = new ServerPortPlaceHolder(routingServerName, routingPortNumber, null, integratedSecurity); From 92a164a7deaf3d65815d79e22d25826891943513 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 27 Feb 2018 15:06:10 -0800 Subject: [PATCH 02/84] 623 change stash --- .../microsoft/sqlserver/jdbc/IOBuffer.java | 7 +- .../sqlserver/jdbc/SQLServerConnection.java | 66 ++++++++++--------- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 82513ea17..2f6c2fe65 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -1589,8 +1589,11 @@ void enableSSL(String host, String trustStoreFileName = con.activeConnectionProperties.getProperty(SQLServerDriverStringProperty.TRUST_STORE.toString()); String trustStorePassword = con.activeConnectionProperties.getProperty(SQLServerDriverStringProperty.TRUST_STORE_PASSWORD.toString()); - String hostNameInCertificate = con.activeConnectionProperties - .getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString()); + String hostNameInCertificate = con.getNewlyRoutedHostName(); + if (null == hostNameInCertificate || hostNameInCertificate.isEmpty()) { + hostNameInCertificate = con.activeConnectionProperties + .getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString()); + } trustStoreType = con.activeConnectionProperties.getProperty(SQLServerDriverStringProperty.TRUST_STORE_TYPE.toString()); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index dcaa9234f..4d9a95da3 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -394,11 +394,16 @@ private enum State { // Contains the routing info received from routing ENVCHANGE private ServerPortPlaceHolder routingInfo = null; + private String newlyRoutedHostName = null; ServerPortPlaceHolder getRoutingInfo() { return routingInfo; } - + + String getNewlyRoutedHostName() { + return newlyRoutedHostName; + } + // Permission targets private static final String callAbortPerm = "callAbort"; @@ -3620,40 +3625,37 @@ final void processEnvChange(TDSReader tdsReader) throws SQLServerException { } // Check if the hostNameInCertificate needs to be updated to handle the rerouted subdomain in Azure - // Do not modify the hostNameInCertificate connection property, since we need this to match the server name - // of Azure SQL Server, which is in the form of .database.windows.net -// String currentHostName = activeConnectionProperties.getProperty("hostNameInCertificate"); -// if (null != currentHostName && currentHostName.startsWith("*") -// && (null != routingServerName) /* skip the check for hostNameInCertificate if routingServerName is null */ -// && routingServerName.indexOf('.') != -1) { -// char[] currentHostNameCharArray = currentHostName.toCharArray(); -// char[] routingServerNameCharArray = routingServerName.toCharArray(); -// boolean hostNameNeedsUpdate = true; -// -// /* -// * Check if routingServerName and hostNameInCertificate are from same domain by verifying each character in currentHostName from -// * last until it reaches the character before the wildcard symbol (i.e. currentHostNameCharArray[1]) -// */ -// for (int i = currentHostName.length() - 1, j = routingServerName.length() - 1; i > 0 && j > 0; i--, j--) { -// if (routingServerNameCharArray[j] != currentHostNameCharArray[i]) { -// hostNameNeedsUpdate = false; -// break; -// } -// } -// -// if (hostNameNeedsUpdate) { -// String newHostName = "*" + routingServerName.substring(routingServerName.indexOf('.')); -// activeConnectionProperties.setProperty("hostNameInCertificate", newHostName); -// -// if (connectionlogger.isLoggable(Level.FINER)) { -// connectionlogger.finer(toString() + "Using new host to validate the SSL certificate"); -// } -// } -// } + String currentHostName = activeConnectionProperties.getProperty("hostNameInCertificate"); + if (null != currentHostName && currentHostName.startsWith("*") + && (null != routingServerName) /* skip the check for hostNameInCertificate if routingServerName is null */ + && routingServerName.indexOf('.') != -1) { + char[] currentHostNameCharArray = currentHostName.toCharArray(); + char[] routingServerNameCharArray = routingServerName.toCharArray(); + boolean hostNameNeedsUpdate = true; + + /* + * Check if routingServerName and hostNameInCertificate are from same domain by verifying each character in currentHostName from + * last until it reaches the character before the wildcard symbol (i.e. currentHostNameCharArray[1]) + */ + for (int i = currentHostName.length() - 1, j = routingServerName.length() - 1; i > 0 && j > 0; i--, j--) { + if (routingServerNameCharArray[j] != currentHostNameCharArray[i]) { + hostNameNeedsUpdate = false; + break; + } + } + + if (hostNameNeedsUpdate) { + String newHostName = "*" + routingServerName.substring(routingServerName.indexOf('.')); + newlyRoutedHostName = newHostName; + + if (connectionlogger.isLoggable(Level.FINER)) { + connectionlogger.finer(toString() + "Using new host to validate the SSL certificate"); + } + } + } isRoutedInCurrentAttempt = true; routingInfo = new ServerPortPlaceHolder(routingServerName, routingPortNumber, null, integratedSecurity); - break; // Error on unrecognized, unused ENVCHANGES From 5e251c9c1080a768834f21b3a012262125587693 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Mon, 5 Mar 2018 21:50:29 -0800 Subject: [PATCH 03/84] Prepared Statement Caching fix for 'handle not found' errors --- .../microsoft/sqlserver/jdbc/SQLServerConnection.java | 4 ++-- .../sqlserver/jdbc/SQLServerPreparedStatement.java | 10 +++++----- .../jdbc/unit/statement/PreparedStatementTest.java | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 432aa9e32..043ca3602 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -122,8 +122,8 @@ static class Sha1HashKey { Sha1HashKey(String sql, String parametersDefinition, - String dbName) { - this(String.format("%s%s%s", sql, parametersDefinition, dbName)); + String dbName, int hashcode) { + this(String.format("%s%s%s%s", sql, parametersDefinition, dbName, hashcode)); } Sha1HashKey(String s) { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 262728a94..26340d4fc 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -533,7 +533,7 @@ final void doExecutePreparedStatement(PrepStmtExecCmd command) throws SQLServerE for (int attempt = 1; attempt <= 2; ++attempt) { try { // Re-use handle if available, requires parameter definitions which are not available until here. - if (reuseCachedHandle(hasNewTypeDefinitions, 1 < attempt, dbName)) { + if (reuseCachedHandle(hasNewTypeDefinitions, 1 < attempt, dbName, this)) { hasNewTypeDefinitions = false; } @@ -606,7 +606,7 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException { } cachedPreparedStatementHandle = connection.registerCachedPreparedStatementHandle( - new Sha1HashKey(preparedSQL, preparedTypeDefinitions, dbName), prepStmtHandle, executedSqlDirectly); + new Sha1HashKey(preparedSQL, preparedTypeDefinitions, dbName, this.hashCode()), prepStmtHandle, executedSqlDirectly); } param.skipValue(tdsReader, true); @@ -928,7 +928,7 @@ private void getParameterEncryptionMetadata(Parameter[] params) throws SQLServer } /** Manage re-using cached handles */ - private boolean reuseCachedHandle(boolean hasNewTypeDefinitions, boolean discardCurrentCacheItem, String dbName) { + private boolean reuseCachedHandle(boolean hasNewTypeDefinitions, boolean discardCurrentCacheItem, String dbName, SQLServerPreparedStatement pstmt) { // No re-use of caching for cursorable statements (statements that WILL use sp_cursor*) if (isCursorable(executeMethod)) return false; @@ -958,8 +958,8 @@ private boolean reuseCachedHandle(boolean hasNewTypeDefinitions, boolean discard // Check for new cache reference. if (null == cachedPreparedStatementHandle) { PreparedStatementHandle cachedHandle = connection - .getCachedPreparedStatementHandle(new Sha1HashKey(preparedSQL, preparedTypeDefinitions, dbName)); + .getCachedPreparedStatementHandle(new Sha1HashKey(preparedSQL, preparedTypeDefinitions, dbName, pstmt.hashCode())); // If handle was found then re-use, only if AE is not on and is not a batch query with new type definitions (We shouldn't reuse handle // if it is batch query and has new type definition, or if it is on, make sure encryptionMetadataIsRetrieved is retrieved. if (null != cachedHandle) { @@ -2635,7 +2635,7 @@ final void doExecutePreparedStatementBatch(PrepStmtBatchExecCmd batchCommand) th try { // Re-use handle if available, requires parameter definitions which are not available until here. - if (reuseCachedHandle(hasNewTypeDefinitions, 1 < attempt, dbName)) { + if (reuseCachedHandle(hasNewTypeDefinitions, 1 < attempt, dbName, this)) { hasNewTypeDefinitions = false; } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java index 58c7969ab..4ea9552f7 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java @@ -272,7 +272,7 @@ public void testStatementPooling() throws SQLException { pstmt.getMoreResults(); // Make sure handle is updated. assertNotSame(0, pstmt.getPreparedStatementHandle()); - assertSame(handle, pstmt.getPreparedStatementHandle()); + assertNotSame(handle, pstmt.getPreparedStatementHandle()); } // Execute new statement with different SQL text and verify it does NOT get same handle (should now fall back to using sp_executesql). From bfa21eb4c65cf8b1149afeab2dee821d276fcdd5 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Wed, 7 Mar 2018 15:42:18 -0800 Subject: [PATCH 04/84] Fix for PS Caching issue - Calling reset instead of on type def changes --- .../sqlserver/jdbc/SQLServerConnection.java | 4 ++-- .../jdbc/SQLServerPreparedStatement.java | 18 +++++++----------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 043ca3602..432aa9e32 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -122,8 +122,8 @@ static class Sha1HashKey { Sha1HashKey(String sql, String parametersDefinition, - String dbName, int hashcode) { - this(String.format("%s%s%s%s", sql, parametersDefinition, dbName, hashcode)); + String dbName) { + this(String.format("%s%s%s", sql, parametersDefinition, dbName)); } Sha1HashKey(String s) { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 26340d4fc..be0641d66 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -533,7 +533,7 @@ final void doExecutePreparedStatement(PrepStmtExecCmd command) throws SQLServerE for (int attempt = 1; attempt <= 2; ++attempt) { try { // Re-use handle if available, requires parameter definitions which are not available until here. - if (reuseCachedHandle(hasNewTypeDefinitions, 1 < attempt, dbName, this)) { + if (reuseCachedHandle(hasNewTypeDefinitions, 1 < attempt, dbName)) { hasNewTypeDefinitions = false; } @@ -606,7 +606,7 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException { } cachedPreparedStatementHandle = connection.registerCachedPreparedStatementHandle( - new Sha1HashKey(preparedSQL, preparedTypeDefinitions, dbName, this.hashCode()), prepStmtHandle, executedSqlDirectly); + new Sha1HashKey(preparedSQL, preparedTypeDefinitions, dbName), prepStmtHandle, executedSqlDirectly); } param.skipValue(tdsReader, true); @@ -928,7 +928,7 @@ private void getParameterEncryptionMetadata(Parameter[] params) throws SQLServer } /** Manage re-using cached handles */ - private boolean reuseCachedHandle(boolean hasNewTypeDefinitions, boolean discardCurrentCacheItem, String dbName, SQLServerPreparedStatement pstmt) { + private boolean reuseCachedHandle(boolean hasNewTypeDefinitions, boolean discardCurrentCacheItem, String dbName) { // No re-use of caching for cursorable statements (statements that WILL use sp_cursor*) if (isCursorable(executeMethod)) return false; @@ -948,18 +948,14 @@ private boolean reuseCachedHandle(boolean hasNewTypeDefinitions, boolean discard // New type definitions and existing cached handle reference then deregister cached handle. if(hasNewTypeDefinitions) { - if (null != cachedPreparedStatementHandle && hasPreparedStatementHandle() && prepStmtHandle == cachedPreparedStatementHandle.getHandle()) { - cachedPreparedStatementHandle.removeReference(); - cachedPreparedStatementHandle.setIsExplicitlyDiscarded(); - } + resetPrepStmtHandle(); cachedPreparedStatementHandle = null; } // Check for new cache reference. if (null == cachedPreparedStatementHandle) { - PreparedStatementHandle cachedHandle = connection - - .getCachedPreparedStatementHandle(new Sha1HashKey(preparedSQL, preparedTypeDefinitions, dbName, pstmt.hashCode())); + PreparedStatementHandle cachedHandle = connection.getCachedPreparedStatementHandle( + new Sha1HashKey(preparedSQL, preparedTypeDefinitions, dbName)); // If handle was found then re-use, only if AE is not on and is not a batch query with new type definitions (We shouldn't reuse handle // if it is batch query and has new type definition, or if it is on, make sure encryptionMetadataIsRetrieved is retrieved. if (null != cachedHandle) { @@ -2635,7 +2631,7 @@ final void doExecutePreparedStatementBatch(PrepStmtBatchExecCmd batchCommand) th try { // Re-use handle if available, requires parameter definitions which are not available until here. - if (reuseCachedHandle(hasNewTypeDefinitions, 1 < attempt, dbName, this)) { + if (reuseCachedHandle(hasNewTypeDefinitions, 1 < attempt, dbName)) { hasNewTypeDefinitions = false; } From c80c93e50d16ea87e45525d7f7a428e20779f8e4 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Wed, 7 Mar 2018 16:45:04 -0800 Subject: [PATCH 05/84] Updated comparison --- .../microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index be0641d66..2267e3aa4 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -327,7 +327,7 @@ final void closeInternal() { private boolean buildPreparedStrings(Parameter[] params, boolean renewDefinition) throws SQLServerException { String newTypeDefinitions = buildParamTypeDefinitions(params, renewDefinition); - if (null != preparedTypeDefinitions && newTypeDefinitions.equals(preparedTypeDefinitions)) + if (null != preparedTypeDefinitions && newTypeDefinitions.equalsIgnoreCase(preparedTypeDefinitions)) return false; preparedTypeDefinitions = newTypeDefinitions; From 7978516456782cf46dd5691818514f858e4c4471 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Wed, 7 Mar 2018 20:50:49 -0800 Subject: [PATCH 06/84] Change back assert check. --- .../sqlserver/jdbc/unit/statement/PreparedStatementTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java index 4ea9552f7..58c7969ab 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java @@ -272,7 +272,7 @@ public void testStatementPooling() throws SQLException { pstmt.getMoreResults(); // Make sure handle is updated. assertNotSame(0, pstmt.getPreparedStatementHandle()); - assertNotSame(handle, pstmt.getPreparedStatementHandle()); + assertSame(handle, pstmt.getPreparedStatementHandle()); } // Execute new statement with different SQL text and verify it does NOT get same handle (should now fall back to using sp_executesql). From f091eff8e05abe5e53e0c8f5969df267638658eb Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Mon, 12 Mar 2018 16:41:18 -0700 Subject: [PATCH 07/84] Adding call to removeReference back + Fix for Batch processes intermittent failures. --- .../sqlserver/jdbc/SQLServerPreparedStatement.java | 13 ++++++++----- .../sqlserver/jdbc/SQLServerStatement.java | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 2267e3aa4..4b9c48d24 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -548,7 +548,7 @@ final void doExecutePreparedStatement(PrepStmtExecCmd command) throws SQLServerE getNextResult(); } catch (SQLException e) { - if (retryBasedOnFailedReuseOfCachedHandle(e, attempt, needsPrepare)) + if (retryBasedOnFailedReuseOfCachedHandle(e, attempt, needsPrepare, false)) continue; else throw e; @@ -566,12 +566,12 @@ else if (EXECUTE_UPDATE == executeMethod && null != resultSet) { /** Should the execution be retried because the re-used cached handle could not be re-used due to server side state changes? */ private boolean retryBasedOnFailedReuseOfCachedHandle(SQLException e, - int attempt, boolean needsPrepare) { + int attempt, boolean needsPrepare, boolean isBatch) { // Only retry based on these error codes and if statementPooling is enabled: // 586: The prepared statement handle %d is not valid in this context. Please verify that current database, user default schema, and // ANSI_NULLS and QUOTED_IDENTIFIER set options are not changed since the handle is prepared. // 8179: Could not find prepared statement with handle %d. - if(needsPrepare) return false; + if(needsPrepare && !isBatch) return false; return 1 == attempt && (586 == e.getErrorCode() || 8179 == e.getErrorCode()) && connection.isStatementPoolingEnabled(); } @@ -948,6 +948,9 @@ private boolean reuseCachedHandle(boolean hasNewTypeDefinitions, boolean discard // New type definitions and existing cached handle reference then deregister cached handle. if(hasNewTypeDefinitions) { + if (null != cachedPreparedStatementHandle && hasPreparedStatementHandle() && prepStmtHandle == cachedPreparedStatementHandle.getHandle()) { + cachedPreparedStatementHandle.removeReference(); + } resetPrepStmtHandle(); cachedPreparedStatementHandle = null; } @@ -2688,7 +2691,7 @@ final void doExecutePreparedStatementBatch(PrepStmtBatchExecCmd batchCommand) th throw e; // Retry if invalid handle exception. - if (retryBasedOnFailedReuseOfCachedHandle(e, attempt, needsPrepare)) { + if (retryBasedOnFailedReuseOfCachedHandle(e, attempt, needsPrepare, true)) { // reset number of batches prepare numBatchesPrepared = numBatchesExecuted; retry = true; @@ -2719,7 +2722,7 @@ final void doExecutePreparedStatementBatch(PrepStmtBatchExecCmd batchCommand) th } } catch (SQLException e) { - if (retryBasedOnFailedReuseOfCachedHandle(e, attempt, needsPrepare) && connection.isStatementPoolingEnabled()) { + if (retryBasedOnFailedReuseOfCachedHandle(e, attempt, needsPrepare, true) && connection.isStatementPoolingEnabled()) { // Reset number of batches prepared. numBatchesPrepared = numBatchesExecuted; continue; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index 7d7608433..9f87e2bb9 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -1236,7 +1236,7 @@ final void processResults() throws SQLServerException { // If statement execution was canceled then continue processing the // remaining results before throwing the "statement canceled" exception. - if (e.getSQLState().equals(SQLState.STATEMENT_CANCELED.getSQLStateCode())) { + if (e.getSQLState()!=null && e.getSQLState().equals(SQLState.STATEMENT_CANCELED.getSQLStateCode())) { interruptException = e; continue; } From 29096548a4d92e8b4f4c5688d11ceac176110b1f Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Tue, 3 Apr 2018 15:20:34 -0700 Subject: [PATCH 08/84] Removed DBName and made changes to resetPrepStmtHandle method --- .../sqlserver/jdbc/SQLServerConnection.java | 9 ++- .../jdbc/SQLServerDatabaseMetaData.java | 4 +- .../jdbc/SQLServerPreparedStatement.java | 67 ++++++++----------- 3 files changed, 35 insertions(+), 45 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 432aa9e32..6429e10bf 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -119,13 +119,12 @@ public class SQLServerConnection implements ISQLServerConnection { static class Sha1HashKey { private byte[] bytes; - + Sha1HashKey(String sql, - String parametersDefinition, - String dbName) { - this(String.format("%s%s%s", sql, parametersDefinition, dbName)); + String parametersDefinition) { + this(String.format("%s%s", sql, parametersDefinition)); } - + Sha1HashKey(String s) { bytes = getSha1Digest().digest(s.getBytes()); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 3ec82dc11..9d20afecd 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -269,11 +269,11 @@ private CallableStatement getCallableStatementHandle(CallableHandles request, hassoc = new HandleAssociation(catalog, CS); HandleAssociation previous = handleMap.put(request, hassoc); if (null != previous) { - ((SQLServerCallableStatement) previous.stmt).handleDBName = previous.databaseName; + //((SQLServerCallableStatement) previous.stmt).handleDBName = previous.databaseName; previous.close(); - ((SQLServerCallableStatement) previous.stmt).handleDBName = null; + //((SQLServerCallableStatement) previous.stmt).handleDBName = null; } } return hassoc.stmt; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 8d1ed8527..1bfc9d828 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -99,7 +99,6 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS /** The prepared statement handle returned by the server */ private int prepStmtHandle = 0; - String handleDBName = null; /** Statement used for getMetadata(). Declared as a field to facilitate closing the statement. */ private SQLServerStatement internalStmt = null; @@ -130,10 +129,18 @@ private boolean hasPreparedStatementHandle() { /** Resets the server handle for this prepared statement to no handle. */ - private void resetPrepStmtHandle() { + private boolean resetPrepStmtHandle(boolean discardCurrentCacheItem) { + boolean statementPoolingUsed = null != cachedPreparedStatementHandle; + // Return to pool and decrement reference count + if (statementPoolingUsed) { + // Make sure the cached handle does not get re-used more. + if(discardCurrentCacheItem) + cachedPreparedStatementHandle.setIsExplicitlyDiscarded(); + } prepStmtHandle = 0; + return statementPoolingUsed; } - + /** Flag set to true when statement execution is expected to return the prepared statement handle */ private boolean expectPrepStmtHandle = false; @@ -216,10 +223,10 @@ private void closePreparedHandle() { else { isExecutedAtLeastOnce = false; final int handleToClose = prepStmtHandle; - resetPrepStmtHandle(); + // Handle unprepare actions through statement pooling. - if (null != cachedPreparedStatementHandle) { + if (resetPrepStmtHandle(false)) { connection.returnCachedPreparedStatementHandle(cachedPreparedStatementHandle); } // If no reference to a statement pool cache item is found handle unprepare actions through batching @ connection level. @@ -533,13 +540,12 @@ final void doExecutePreparedStatement(PrepStmtExecCmd command) throws SQLServerE hasNewTypeDefinitions = buildPreparedStrings(inOutParam, true); } - String dbName = connection.getSCatalog(); boolean needsPrepare = true; // Retry execution if existing handle could not be re-used. for (int attempt = 1; attempt <= 2; ++attempt) { try { // Re-use handle if available, requires parameter definitions which are not available until here. - if (reuseCachedHandle(hasNewTypeDefinitions, 1 < attempt, dbName)) { + if (reuseCachedHandle(hasNewTypeDefinitions, 1 < attempt)) { hasNewTypeDefinitions = false; } @@ -606,13 +612,8 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException { // Cache the reference to the newly created handle, NOT for cursorable handles. if (null == cachedPreparedStatementHandle && !isCursorable(executeMethod)) { - String dbName = connection.getSCatalog(); - if (null != handleDBName) { - dbName = handleDBName; - } - cachedPreparedStatementHandle = connection.registerCachedPreparedStatementHandle( - new Sha1HashKey(preparedSQL, preparedTypeDefinitions, dbName), prepStmtHandle, executedSqlDirectly); + new Sha1HashKey(preparedSQL, preparedTypeDefinitions), prepStmtHandle, executedSqlDirectly); } param.skipValue(tdsReader, true); @@ -666,7 +667,7 @@ private void buildServerCursorPrepExecParams(TDSWriter tdsWriter) throws SQLServ // IN (reprepare): Old handle to unprepare before repreparing // OUT: The newly prepared handle tdsWriter.writeRPCInt(null, getPreparedStatementHandle(), true); - resetPrepStmtHandle(); + resetPrepStmtHandle(false); // OUT tdsWriter.writeRPCInt(null, 0, true); // cursor ID (OUTPUT) @@ -708,7 +709,7 @@ private void buildPrepExecParams(TDSWriter tdsWriter) throws SQLServerException // IN (reprepare): Old handle to unprepare before repreparing // OUT: The newly prepared handle tdsWriter.writeRPCInt(null, getPreparedStatementHandle(), true); - resetPrepStmtHandle(); + resetPrepStmtHandle(false); // IN tdsWriter.writeRPCStringUnicode((preparedTypeDefinitions.length() > 0) ? preparedTypeDefinitions : null); @@ -732,7 +733,7 @@ private void buildExecSQLParams(TDSWriter tdsWriter) throws SQLServerException { tdsWriter.writeByte((byte) 0); // RPC procedure option 2 // No handle used. - resetPrepStmtHandle(); + resetPrepStmtHandle(false); // IN tdsWriter.writeRPCStringUnicode(preparedSQL); @@ -934,37 +935,28 @@ private void getParameterEncryptionMetadata(Parameter[] params) throws SQLServer } /** Manage re-using cached handles */ - private boolean reuseCachedHandle(boolean hasNewTypeDefinitions, boolean discardCurrentCacheItem, String dbName) { + private boolean reuseCachedHandle(boolean hasNewTypeDefinitions, boolean discardCurrentCacheItem) { // No re-use of caching for cursorable statements (statements that WILL use sp_cursor*) if (isCursorable(executeMethod)) return false; - // If current cache item should be discarded make sure it is not used again. - if (discardCurrentCacheItem && null != cachedPreparedStatementHandle) { - - cachedPreparedStatementHandle.removeReference(); - - // Make sure the cached handle does not get re-used more. - resetPrepStmtHandle(); - cachedPreparedStatementHandle.setIsExplicitlyDiscarded(); - cachedPreparedStatementHandle = null; - - return false; - } - - // New type definitions and existing cached handle reference then deregister cached handle. - if(hasNewTypeDefinitions) { - if (null != cachedPreparedStatementHandle && hasPreparedStatementHandle() && prepStmtHandle == cachedPreparedStatementHandle.getHandle()) { + // If current cache items needs to be discarded or New type definitions found with existing cached handle reference then deregister cached handle. + if (discardCurrentCacheItem || hasNewTypeDefinitions) { + if (discardCurrentCacheItem || (null != cachedPreparedStatementHandle && hasPreparedStatementHandle() && prepStmtHandle == cachedPreparedStatementHandle.getHandle())) { cachedPreparedStatementHandle.removeReference(); } - resetPrepStmtHandle(); - cachedPreparedStatementHandle = null; + + // Make sure the cached handle does not get re-used more if it should be discarded + resetPrepStmtHandle(discardCurrentCacheItem); + cachedPreparedStatementHandle = null; + if(discardCurrentCacheItem) + return false; } // Check for new cache reference. if (null == cachedPreparedStatementHandle) { PreparedStatementHandle cachedHandle = connection.getCachedPreparedStatementHandle( - new Sha1HashKey(preparedSQL, preparedTypeDefinitions, dbName)); + new Sha1HashKey(preparedSQL, preparedTypeDefinitions)); // If handle was found then re-use, only if AE is not on and is not a batch query with new type definitions (We shouldn't reuse handle // if it is batch query and has new type definition, or if it is on, make sure encryptionMetadataIsRetrieved is retrieved. if (null != cachedHandle) { @@ -2651,14 +2643,13 @@ final void doExecutePreparedStatementBatch(PrepStmtBatchExecCmd batchCommand) th } } - String dbName = connection.getSCatalog(); boolean needsPrepare = true; // Retry execution if existing handle could not be re-used. for (int attempt = 1; attempt <= 2; ++attempt) { try { // Re-use handle if available, requires parameter definitions which are not available until here. - if (reuseCachedHandle(hasNewTypeDefinitions, 1 < attempt, dbName)) { + if (reuseCachedHandle(hasNewTypeDefinitions, 1 < attempt)) { hasNewTypeDefinitions = false; } From c3365fb3628dd6fff73cb68f19bdc1c17e5adf08 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Thu, 5 Apr 2018 10:56:06 -0700 Subject: [PATCH 09/84] Check for null handle before proceed --- .../microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 1bfc9d828..b1272405b 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -942,7 +942,7 @@ private boolean reuseCachedHandle(boolean hasNewTypeDefinitions, boolean discard // If current cache items needs to be discarded or New type definitions found with existing cached handle reference then deregister cached handle. if (discardCurrentCacheItem || hasNewTypeDefinitions) { - if (discardCurrentCacheItem || (null != cachedPreparedStatementHandle && hasPreparedStatementHandle() && prepStmtHandle == cachedPreparedStatementHandle.getHandle())) { + if (null != cachedPreparedStatementHandle && (discardCurrentCacheItem || (hasPreparedStatementHandle() && prepStmtHandle == cachedPreparedStatementHandle.getHandle()))) { cachedPreparedStatementHandle.removeReference(); } From 0b1d915f85e95a96646fcfca6f79c3910edfa361 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Thu, 12 Apr 2018 12:43:26 -0700 Subject: [PATCH 10/84] Adding Old Constrcutor back to AKV Implementation --- .../sqlserver/jdbc/KeyVaultCredential.java | 40 +++++++++++++++-- ...ColumnEncryptionAzureKeyVaultProvider.java | 45 ++++++++++++++++--- ...LServerKeyVaultAuthenticationCallback.java | 27 +++++++++++ 3 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/SQLServerKeyVaultAuthenticationCallback.java diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java b/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java index d0b5693c3..6146edf14 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java @@ -8,6 +8,7 @@ package com.microsoft.sqlserver.jdbc; +import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -24,20 +25,30 @@ */ class KeyVaultCredential extends KeyVaultCredentials { + SQLServerKeyVaultAuthenticationCallback authenticationCallback = null; String clientId = null; String clientKey = null; + String accessToken = null; KeyVaultCredential(String clientId, String clientKey) { this.clientId = clientId; this.clientKey = clientKey; } - + + KeyVaultCredential(SQLServerKeyVaultAuthenticationCallback authenticationCallback) { + this.authenticationCallback = authenticationCallback; + } + public String doAuthenticate(String authorization, String resource, String scope) { - AuthenticationResult token = getAccessTokenFromClientCredentials(authorization, resource, clientId, clientKey); - return token.getAccessToken(); + if(authenticationCallback==null) { + AuthenticationResult token = getAccessTokenFromClientCredentials(authorization, resource, clientId, clientKey); + return token.getAccessToken(); + }else { + return authenticationCallback.getAccessToken(authorization, resource, scope); + } } private static AuthenticationResult getAccessTokenFromClientCredentials(String authorization, @@ -66,4 +77,27 @@ private static AuthenticationResult getAccessTokenFromClientCredentials(String a } return result; } + + /** + * Authenticates the service request + * + * @param request + * the ServiceRequestContext + * @param challenge + * used to get the accessToken + * @return BasicHeader + */ + public String doAuthenticate(Map challenge) { + assert null != challenge; + + String authorization = challenge.get("authorization"); + String resource = challenge.get("resource"); + + accessToken = authenticationCallback.getAccessToken(authorization, resource, ""); + return accessToken; //new BasicHeader("Authorization", accessTokenType + " " + accessToken); + } + + void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java index c6e168a85..0116632ec 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java @@ -18,13 +18,20 @@ import java.security.NoSuchAlgorithmException; import java.text.MessageFormat; import java.util.Locale; +import java.util.concurrent.ExecutorService; +import com.microsoft.azure.AzureResponseBuilder; import com.microsoft.azure.keyvault.KeyVaultClient; import com.microsoft.azure.keyvault.models.KeyBundle; import com.microsoft.azure.keyvault.models.KeyOperationResult; import com.microsoft.azure.keyvault.models.KeyVerifyResult; import com.microsoft.azure.keyvault.webkey.JsonWebKeyEncryptionAlgorithm; import com.microsoft.azure.keyvault.webkey.JsonWebKeySignatureAlgorithm; +import com.microsoft.azure.serializer.AzureJacksonAdapter; +import com.microsoft.rest.RestClient; + +import okhttp3.OkHttpClient; +import retrofit2.Retrofit; /** * Provides implementation similar to certificate store provider. A CEK encrypted with certificate store provider should be decryptable by this @@ -41,7 +48,7 @@ public class SQLServerColumnEncryptionAzureKeyVaultProvider extends SQLServerCol * Column Encryption Key Store Provider string */ String name = "AZURE_KEY_VAULT"; - + private static String baseUrl = "https://{vaultBaseUrl}"; private final String azureKeyVaultDomainName = "vault.azure.net"; private final String rsaEncryptionAlgorithmWithOAEPForAKV = "RSA-OAEP"; @@ -53,7 +60,7 @@ public class SQLServerColumnEncryptionAzureKeyVaultProvider extends SQLServerCol private KeyVaultClient keyVaultClient; - private KeyVaultCredential credential; + private KeyVaultCredential credentials; public void setName(String name) { this.name = name; @@ -62,7 +69,35 @@ public void setName(String name) { public String getName() { return this.name; } - + + /** + * Constructor that takes a callback function to authenticate to AAD. This is used by KeyVaultClient at runtime to authenticate to Azure Key + * Vault. + * + * @param authenticationCallback + * - Callback function used for authenticating to AAD. + * @param executorService + * - The ExecutorService used to create the keyVaultClient + * @throws SQLServerException + * when an error occurs + */ + public SQLServerColumnEncryptionAzureKeyVaultProvider(SQLServerKeyVaultAuthenticationCallback authenticationCallback, + ExecutorService executorService) throws SQLServerException { + if (null == authenticationCallback) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); + Object[] msgArgs1 = {"SQLServerKeyVaultAuthenticationCallback"}; + throw new SQLServerException(form.format(msgArgs1), null); + } + credentials = new KeyVaultCredential(authenticationCallback); + RestClient restClient = new RestClient.Builder(new OkHttpClient.Builder(), new Retrofit.Builder()) + .withBaseUrl(baseUrl) + .withCredentials(credentials) + .withSerializerAdapter(new AzureJacksonAdapter()) + .withResponseBuilderFactory(new AzureResponseBuilder.Factory()) + .build(); + keyVaultClient = new KeyVaultClient(restClient); + } + /** * Constructor that authenticates to AAD. This is used by KeyVaultClient at runtime to authenticate to Azure Key * Vault. @@ -76,8 +111,8 @@ public String getName() { */ public SQLServerColumnEncryptionAzureKeyVaultProvider(String clientId, String clientKey) throws SQLServerException { - credential = new KeyVaultCredential(clientId, clientKey); - keyVaultClient = new KeyVaultClient(credential); + credentials = new KeyVaultCredential(clientId, clientKey); + keyVaultClient = new KeyVaultClient(credentials); } /** diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerKeyVaultAuthenticationCallback.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerKeyVaultAuthenticationCallback.java new file mode 100644 index 000000000..dc4bddda1 --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerKeyVaultAuthenticationCallback.java @@ -0,0 +1,27 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + +package com.microsoft.sqlserver.jdbc; + +public interface SQLServerKeyVaultAuthenticationCallback { + + /** + * The authentication callback delegate which is to be implemented by the client code + * + * @param authority + * - Identifier of the authority, a URL. + * @param resource + * - Identifier of the target resource that is the recipient of the requested token, a URL. + * @param scope + * - The scope of the authentication request. + * @return access token + */ + public String getAccessToken(String authority, + String resource, + String scope); +} \ No newline at end of file From 43f6e911667c4fa02b75faf4d095a1f0647acd41 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Thu, 12 Apr 2018 13:22:57 -0700 Subject: [PATCH 11/84] Making baseURL final --- .../jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java index 0116632ec..b307738b7 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java @@ -48,7 +48,9 @@ public class SQLServerColumnEncryptionAzureKeyVaultProvider extends SQLServerCol * Column Encryption Key Store Provider string */ String name = "AZURE_KEY_VAULT"; - private static String baseUrl = "https://{vaultBaseUrl}"; + + private final String baseUrl = "https://{vaultBaseUrl}"; + private final String azureKeyVaultDomainName = "vault.azure.net"; private final String rsaEncryptionAlgorithmWithOAEPForAKV = "RSA-OAEP"; From 433abda59f01e2fb880995c5f977e3232cb70852 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Thu, 12 Apr 2018 13:30:11 -0700 Subject: [PATCH 12/84] Remove unnecessary code. --- .../sqlserver/jdbc/KeyVaultCredential.java | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java b/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java index 6146edf14..c13354da7 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java @@ -8,7 +8,6 @@ package com.microsoft.sqlserver.jdbc; -import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -77,27 +76,4 @@ private static AuthenticationResult getAccessTokenFromClientCredentials(String a } return result; } - - /** - * Authenticates the service request - * - * @param request - * the ServiceRequestContext - * @param challenge - * used to get the accessToken - * @return BasicHeader - */ - public String doAuthenticate(Map challenge) { - assert null != challenge; - - String authorization = challenge.get("authorization"); - String resource = challenge.get("resource"); - - accessToken = authenticationCallback.getAccessToken(authorization, resource, ""); - return accessToken; //new BasicHeader("Authorization", accessTokenType + " " + accessToken); - } - - void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } } From b727aa09b845be78e927ce95baa7b88503b63d62 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Fri, 20 Apr 2018 10:54:44 -0700 Subject: [PATCH 13/84] Use Bulk Copy API for batch insert operation --- .../microsoft/sqlserver/jdbc/Parameter.java | 4 + .../jdbc/SQLServerBulkBatchInsertRecord.java | 599 ++++++++++++++++++ .../sqlserver/jdbc/SQLServerConnection.java | 35 + .../jdbc/SQLServerPreparedStatement.java | 363 ++++++++++- .../sqlserver/jdbc/SQLServerStatement.java | 22 + 5 files changed, 1022 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java index b614ad5fe..2a9c757d0 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java @@ -415,6 +415,10 @@ Object getValue(JDBCType jdbcType, // statement level), cryptoMeta would be null. return getterDTV.getValue(jdbcType, outScale, getterArgs, cal, typeInfo, cryptoMeta, tdsReader); } + + Object getSetterValue() { + return setterDTV.getSetterValue(); + } int getInt(TDSReader tdsReader) throws SQLServerException { Integer value = (Integer) getValue(JDBCType.INTEGER, null, null, tdsReader); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java new file mode 100644 index 000000000..f987ad821 --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java @@ -0,0 +1,599 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + +package com.microsoft.sqlserver.jdbc; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.Types; +import java.text.DecimalFormat; +import java.text.MessageFormat; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * A simple implementation of the ISQLServerBulkRecord interface that can be used to read in the basic Java data types from an ArrayList of + * Parameters that were provided by pstmt/cstmt. + */ +public class SQLServerBulkBatchInsertRecord implements ISQLServerBulkRecord, java.lang.AutoCloseable { + /* + * Class to represent the column metadata + */ + private class ColumnMetadata { + String columnName; + int columnType; + int precision; + int scale; + DateTimeFormatter dateTimeFormatter = null; + + ColumnMetadata(String name, + int type, + int precision, + int scale, + DateTimeFormatter dateTimeFormatter) { + columnName = name; + columnType = type; + this.precision = precision; + this.scale = scale; + this.dateTimeFormatter = dateTimeFormatter; + } + } + + /* + * Metadata to represent the columns in the batch. Each column should be mapped to its corresponding position within the parameter (from position 1 and + * onwards) + */ + private Map columnMetadata; + + /* + * Contains all the column names if firstLineIsColumnNames is true + */ + private String[] columnNames = null; + + /* + * Contains the format that java.sql.Types.TIMESTAMP_WITH_TIMEZONE data should be read in as. + */ + private DateTimeFormatter dateTimeFormatter = null; + + /* + * Contains the format that java.sql.Types.TIME_WITH_TIMEZONE data should be read in as. + */ + private DateTimeFormatter timeFormatter = null; + + /* + * Class name for logging. + */ + private static final String loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkBatchInsertRecord"; + + /* + * Logger + */ + private static final java.util.logging.Logger loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); + + private ArrayList batchParam; + private int batchParamIndex = 0; + private ArrayList columnList; + private ArrayList valueList; + + public SQLServerBulkBatchInsertRecord(ArrayList batchParam, ArrayList columnList, + ArrayList valueList, String encoding) throws SQLServerException { + loggerExternal.entering(loggerClassName, "SQLServerBulkBatchInsertRecord", + new Object[] {batchParam, encoding}); + + if (null == batchParam) { + throwInvalidArgument("batchParam"); + } + + if (null == valueList) { + throwInvalidArgument("valueList"); + } + + this.batchParam = batchParam; + this.columnList = columnList; + this.valueList = valueList; + columnMetadata = new HashMap<>(); + + loggerExternal.exiting(loggerClassName, "SQLServerBulkBatchInsertRecord"); + } + + /** + * Adds metadata for the given column in the file. + * + * @param positionInTable + * Indicates which column the metadata is for. Columns start at 1. + * @param name + * Name for the column (optional if only using column ordinal in a mapping for SQLServerBulkCopy operation) + * @param jdbcType + * JDBC data type of the column + * @param precision + * Precision for the column (ignored for the appropriate data types) + * @param scale + * Scale for the column (ignored for the appropriate data types) + * @param dateTimeFormatter + * format to parse data that is sent + * @throws SQLServerException + * when an error occurs + */ + public void addColumnMetadata(int positionInTable, + String name, + int jdbcType, + int precision, + int scale, + DateTimeFormatter dateTimeFormatter) throws SQLServerException { + addColumnMetadataInternal(positionInTable, name, jdbcType, precision, scale, dateTimeFormatter); + } + + /** + * Adds metadata for the given column in the file. + * + * @param positionInTable + * Indicates which column the metadata is for. Columns start at 1. + * @param name + * Name for the column (optional if only using column ordinal in a mapping for SQLServerBulkCopy operation) + * @param jdbcType + * JDBC data type of the column + * @param precision + * Precision for the column (ignored for the appropriate data types) + * @param scale + * Scale for the column (ignored for the appropriate data types) + * @throws SQLServerException + * when an error occurs + */ + public void addColumnMetadata(int positionInTable, + String name, + int jdbcType, + int precision, + int scale) throws SQLServerException { + addColumnMetadataInternal(positionInTable, name, jdbcType, precision, scale, null); + } + + /** + * Adds metadata for the given column in the file. + * + * @param positionInTable + * Indicates which column the metadata is for. Columns start at 1. + * @param name + * Name for the column (optional if only using column ordinal in a mapping for SQLServerBulkCopy operation) + * @param jdbcType + * JDBC data type of the column + * @param precision + * Precision for the column (ignored for the appropriate data types) + * @param scale + * Scale for the column (ignored for the appropriate data types) + * @param dateTimeFormatter + * format to parse data that is sent + * @throws SQLServerException + * when an error occurs + */ + void addColumnMetadataInternal(int positionInTable, + String name, + int jdbcType, + int precision, + int scale, + DateTimeFormatter dateTimeFormatter) throws SQLServerException { + loggerExternal.entering(loggerClassName, "addColumnMetadata", new Object[] {positionInTable, name, jdbcType, precision, scale}); + + String colName = ""; + + if (0 >= positionInTable) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumnOrdinal")); + Object[] msgArgs = {positionInTable}; + throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); + } + + if (null != name) + colName = name.trim(); + else if ((columnNames != null) && (columnNames.length >= positionInTable)) + colName = columnNames[positionInTable - 1]; + + if ((columnNames != null) && (positionInTable > columnNames.length)) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumn")); + Object[] msgArgs = {positionInTable}; + throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); + } + + checkDuplicateColumnName(positionInTable, name); + switch (jdbcType) { + /* + * SQL Server supports numerous string literal formats for temporal types, hence sending them as varchar with approximate + * precision(length) needed to send supported string literals. string literal formats supported by temporal types are available in MSDN + * page on data types. + */ + case java.sql.Types.DATE: + case java.sql.Types.TIME: + case java.sql.Types.TIMESTAMP: + case microsoft.sql.Types.DATETIMEOFFSET: + // The precision is just a number long enough to hold all types of temporal data, doesn't need to be exact precision. + columnMetadata.put(positionInTable, new ColumnMetadata(colName, jdbcType, 50, scale, dateTimeFormatter)); + break; + + // Redirect SQLXML as LONGNVARCHAR + // SQLXML is not valid type in TDS + case java.sql.Types.SQLXML: + columnMetadata.put(positionInTable, new ColumnMetadata(colName, java.sql.Types.LONGNVARCHAR, precision, scale, dateTimeFormatter)); + break; + + // Redirecting Float as Double based on data type mapping + // https://msdn.microsoft.com/en-us/library/ms378878%28v=sql.110%29.aspx + case java.sql.Types.FLOAT: + columnMetadata.put(positionInTable, new ColumnMetadata(colName, java.sql.Types.DOUBLE, precision, scale, dateTimeFormatter)); + break; + + // redirecting BOOLEAN as BIT + case java.sql.Types.BOOLEAN: + columnMetadata.put(positionInTable, new ColumnMetadata(colName, java.sql.Types.BIT, precision, scale, dateTimeFormatter)); + break; + + default: + columnMetadata.put(positionInTable, new ColumnMetadata(colName, jdbcType, precision, scale, dateTimeFormatter)); + } + + loggerExternal.exiting(loggerClassName, "addColumnMetadata"); + } + + /** + * Set the format for reading in dates from the file. + * + * @param dateTimeFormat + * format to parse data sent as java.sql.Types.TIMESTAMP_WITH_TIMEZONE + */ + public void setTimestampWithTimezoneFormat(String dateTimeFormat) { + DriverJDBCVersion.checkSupportsJDBC42(); + loggerExternal.entering(loggerClassName, "setTimestampWithTimezoneFormat", dateTimeFormat); + + this.dateTimeFormatter = DateTimeFormatter.ofPattern(dateTimeFormat); + + loggerExternal.exiting(loggerClassName, "setTimestampWithTimezoneFormat"); + } + + /** + * Set the format for reading in dates from the file. + * + * @param dateTimeFormatter + * format to parse data sent as java.sql.Types.TIMESTAMP_WITH_TIMEZONE + */ + public void setTimestampWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { + loggerExternal.entering(loggerClassName, "setTimestampWithTimezoneFormat", new Object[] {dateTimeFormatter}); + + this.dateTimeFormatter = dateTimeFormatter; + + loggerExternal.exiting(loggerClassName, "setTimestampWithTimezoneFormat"); + } + + /** + * Set the format for reading in dates from the file. + * + * @param timeFormat + * format to parse data sent as java.sql.Types.TIME_WITH_TIMEZONE + */ + public void setTimeWithTimezoneFormat(String timeFormat) { + DriverJDBCVersion.checkSupportsJDBC42(); + loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", timeFormat); + + this.timeFormatter = DateTimeFormatter.ofPattern(timeFormat); + + loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); + } + + /** + * Set the format for reading in dates from the file. + * + * @param dateTimeFormatter + * format to parse data sent as java.sql.Types.TIME_WITH_TIMEZONE + */ + public void setTimeWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { + loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", new Object[] {dateTimeFormatter}); + + this.timeFormatter = dateTimeFormatter; + + loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); + } + + /** + * Releases any resources associated with the file reader. + * + * @throws SQLServerException + * when an error occurs + */ + public void close() throws SQLServerException { + } + + public DateTimeFormatter getColumnDateTimeFormatter(int column) { + return columnMetadata.get(column).dateTimeFormatter; + } + + @Override + public Set getColumnOrdinals() { + return columnMetadata.keySet(); + } + + @Override + public String getColumnName(int column) { + return columnMetadata.get(column).columnName; + } + + @Override + public int getColumnType(int column) { + return columnMetadata.get(column).columnType; + } + + @Override + public int getPrecision(int column) { + return columnMetadata.get(column).precision; + } + + @Override + public int getScale(int column) { + return columnMetadata.get(column).scale; + } + + @Override + public boolean isAutoIncrement(int column) { + return false; + } + + @Override + public Object[] getRowData() throws SQLServerException { + + Object[] data = new Object[columnMetadata.size()]; + + if (null == columnList || columnList.size() == 0) { + int valueIndex = 0; + for (int i = 0; i < data.length; i++) { + if (valueList.get(i).equalsIgnoreCase("?")) { + data[i] = batchParam.get(i)[valueIndex].getSetterValue(); + valueIndex++; + } else { + // remove 's at the beginning and end of the value, if it exists. + int len = valueList.get(i).length(); + if (valueList.get(i).charAt(0) == '\'' && valueList.get(i).charAt(len - 1) == '\'') { + data[i] = valueList.get(i).substring(1, len - 1); + } else { + data[i] = valueList.get(i); + } + } + } + } else { + int valueIndex = 0; + int columnListIndex = 0; + for (int i = 0; i < data.length; i++) { + if (columnList.size() > columnListIndex && columnList.get(columnListIndex).equals(columnMetadata.get(i + 1).columnName)) { + if (valueList.get(i).equalsIgnoreCase("?")) { + data[i] = batchParam.get(i)[valueIndex].getSetterValue(); + valueIndex++; + } else { + // remove 's at the beginning and end of the value, if it exists. + int len = valueList.get(i).length(); + if (valueList.get(i).charAt(0) == '\'' && valueList.get(i).charAt(len - 1) == '\'') { + data[i] = valueList.get(i).substring(1, len - 1); + } else { + data[i] = valueList.get(i); + } + } + columnListIndex++; + } else { + data[i] = ""; + } + } + } + + batchParamIndex++; + + // Cannot go directly from String[] to Object[] and expect it to act as an array. + Object[] dataRow = new Object[data.length]; + + for (Entry pair : columnMetadata.entrySet()) { + ColumnMetadata cm = pair.getValue(); + + if (data.length < pair.getKey() - 1) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumn")); + Object[] msgArgs = {pair.getKey()}; + throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); + } + + // Source header has more columns than current param read + if (columnNames != null && (columnNames.length > data.length)) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_CSVDataSchemaMismatch")); + Object[] msgArgs = {}; + throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); + } + + try { + if (0 == data[pair.getKey() - 1].toString().length()) { + dataRow[pair.getKey() - 1] = null; + continue; + } + + switch (cm.columnType) { + /* + * Both BCP and BULK INSERT considers double quotes as part of the data and throws error if any data (say "10") is to be + * inserted into an numeric column. Our implementation does the same. + */ + case Types.INTEGER: { + // Formatter to remove the decimal part as SQL Server floors the decimal in integer types + DecimalFormat decimalFormatter = new DecimalFormat("#"); + String formatedfInput = decimalFormatter.format(Double.parseDouble(data[pair.getKey() - 1].toString())); + dataRow[pair.getKey() - 1] = Integer.valueOf(formatedfInput); + break; + } + + case Types.TINYINT: + case Types.SMALLINT: { + // Formatter to remove the decimal part as SQL Server floors the decimal in integer types + DecimalFormat decimalFormatter = new DecimalFormat("#"); + String formatedfInput = decimalFormatter.format(Double.parseDouble(data[pair.getKey() - 1].toString())); + dataRow[pair.getKey() - 1] = Short.valueOf(formatedfInput); + break; + } + + case Types.BIGINT: { + BigDecimal bd = new BigDecimal(data[pair.getKey() - 1].toString().trim()); + try { + dataRow[pair.getKey() - 1] = bd.setScale(0, RoundingMode.DOWN).longValueExact(); + } 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, ex); + } + break; + } + + case Types.DECIMAL: + case Types.NUMERIC: { + BigDecimal bd = new BigDecimal(data[pair.getKey() - 1].toString().trim()); + dataRow[pair.getKey() - 1] = bd.setScale(cm.scale, RoundingMode.HALF_UP); + break; + } + + case Types.BIT: { + // "true" => 1, "false" => 0 + // Any non-zero value (integer/double) => 1, 0/0.0 => 0 + try { + dataRow[pair.getKey() - 1] = (0 == Double.parseDouble(data[pair.getKey() - 1].toString())) ? Boolean.FALSE : Boolean.TRUE; + } catch (NumberFormatException e) { + dataRow[pair.getKey() - 1] = Boolean.parseBoolean(data[pair.getKey() - 1].toString()); + } + break; + } + + case Types.REAL: { + dataRow[pair.getKey() - 1] = Float.parseFloat(data[pair.getKey() - 1].toString()); + break; + } + + case Types.DOUBLE: { + dataRow[pair.getKey() - 1] = Double.parseDouble(data[pair.getKey() - 1].toString()); + break; + } + + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + case Types.BLOB: { + /* + * For binary data, the value in file may or may not have the '0x' prefix. We will try to match our implementation with + * 'BULK INSERT' except that we will allow 0x prefix whereas 'BULK INSERT' command does not allow 0x prefix. A BULK INSERT + * example: A sample csv file containing data for 2 binary columns and 1 row: 61,62 Table definition: create table t1(c1 + * varbinary(10), c2 varbinary(10)) BULK INSERT command: bulk insert t1 from 'C:\in.csv' + * with(DATAFILETYPE='char',firstrow=1,FIELDTERMINATOR=',') select * from t1 shows 1 row with columns: 0x61, 0x62 + */ + // Strip off 0x if present. + String binData = data[pair.getKey() - 1].toString().trim(); + if (binData.startsWith("0x") || binData.startsWith("0X")) { + dataRow[pair.getKey() - 1] = binData.substring(2); + } else { + dataRow[pair.getKey() - 1] = binData; + } + break; + } + + case 2013: // java.sql.Types.TIME_WITH_TIMEZONE + { + DriverJDBCVersion.checkSupportsJDBC42(); + OffsetTime offsetTimeValue; + + // The per-column DateTimeFormatter gets priority. + if (null != cm.dateTimeFormatter) + offsetTimeValue = OffsetTime.parse(data[pair.getKey() - 1].toString(), cm.dateTimeFormatter); + else if (timeFormatter != null) + offsetTimeValue = OffsetTime.parse(data[pair.getKey() - 1].toString(), timeFormatter); + else + offsetTimeValue = OffsetTime.parse(data[pair.getKey() - 1].toString()); + + dataRow[pair.getKey() - 1] = offsetTimeValue; + break; + } + + case 2014: // java.sql.Types.TIMESTAMP_WITH_TIMEZONE + { + DriverJDBCVersion.checkSupportsJDBC42(); + OffsetDateTime offsetDateTimeValue; + + // The per-column DateTimeFormatter gets priority. + if (null != cm.dateTimeFormatter) + offsetDateTimeValue = OffsetDateTime.parse(data[pair.getKey() - 1].toString(), cm.dateTimeFormatter); + else if (dateTimeFormatter != null) + offsetDateTimeValue = OffsetDateTime.parse(data[pair.getKey() - 1].toString(), dateTimeFormatter); + else + offsetDateTimeValue = OffsetDateTime.parse(data[pair.getKey() - 1].toString()); + + dataRow[pair.getKey() - 1] = offsetDateTimeValue; + break; + } + + case Types.NULL: { + dataRow[pair.getKey() - 1] = null; + break; + } + + case Types.DATE: + case Types.CHAR: + case Types.NCHAR: + case Types.VARCHAR: + case Types.NVARCHAR: + case Types.LONGVARCHAR: + case Types.LONGNVARCHAR: + case Types.CLOB: + default: { + // The string is copied as is. + dataRow[pair.getKey() - 1] = data[pair.getKey() - 1]; + break; + } + } + } 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, e); + } catch (ArrayIndexOutOfBoundsException e) { + throw new SQLServerException(SQLServerException.getErrString("R_CSVDataSchemaMismatch"), e); + } + + } + return dataRow; + } + + @Override + public boolean next() throws SQLServerException { + return batchParamIndex < batchParam.size(); + } + + /* + * Helper method to throw a SQLServerExeption with the invalidArgument message and given argument. + */ + private void throwInvalidArgument(String argument) throws SQLServerException { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument")); + Object[] msgArgs = {argument}; + SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, false); + } + + /* + * Method to throw a SQLServerExeption for duplicate column names + */ + private void checkDuplicateColumnName(int positionInTable, + String colName) throws SQLServerException { + + if (null != colName && colName.trim().length() != 0) { + for (Entry entry : columnMetadata.entrySet()) { + // duplicate check is not performed in case of same positionInTable value + if (null != entry && entry.getKey() != positionInTable) { + if (null != entry.getValue() && colName.trim().equalsIgnoreCase(entry.getValue().columnName)) { + throw new SQLServerException(SQLServerException.getErrString("R_BulkCSVDataDuplicateColumn"), null); + } + } + + } + } + + } +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 432aa9e32..9c652424c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -116,6 +116,8 @@ public class SQLServerConnection implements ISQLServerConnection { private byte[] accessTokenInByte = null; private SqlFedAuthToken fedAuthToken = null; + + private Boolean isAzureDW = null; static class Sha1HashKey { private byte[] bytes; @@ -5829,6 +5831,39 @@ public void onEviction(Sha1HashKey key, PreparedStatementHandle handle) { } } } + + boolean isAzureDW() throws SQLServerException, SQLException { + if (null == isAzureDW) { + try (Statement stmt = this.createStatement(); ResultSet rs = stmt.executeQuery("SELECT CAST(SERVERPROPERTY('EngineEdition') as INT)");) + { + // SERVERPROPERTY('EngineEdition') can be used to determine whether the db server is SQL Azure. + // It should return 6 for SQL Azure DW. This is more reliable than @@version or serverproperty('edition'). + // Reference: http://msdn.microsoft.com/en-us/library/ee336261.aspx + // + // SERVERPROPERTY('EngineEdition') means + // Database Engine edition of the instance of SQL Server installed on the server. + // 1 = Personal or Desktop Engine (Not available for SQL Server.) + // 2 = Standard (This is returned for Standard and Workgroup.) + // 3 = Enterprise (This is returned for Enterprise, Enterprise Evaluation, and Developer.) + // 4 = Express (This is returned for Express, Express with Advanced Services, and Windows Embedded SQL.) + // 5 = SQL Azure + // 6 = SQL Azure DW + // Base data type: int + final int ENGINE_EDITION_FOR_SQL_AZURE_DW = 6; + rs.next(); + int engineEdition = rs.getInt(1); + if (engineEdition == ENGINE_EDITION_FOR_SQL_AZURE_DW) + { + isAzureDW = true; + } else { + isAzureDW = false; + } + } + return isAzureDW; + } else { + return isAzureDW; + } + } } // Helper class for security manager functions used by SQLServerConnection class. diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 7106d9f10..a2ece7137 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -142,6 +142,8 @@ private void resetPrepStmtHandle() { */ private boolean encryptionMetadataIsRetrieved = false; + private String localUserSQL; + // Internal function used in tracing String getClassNameInternal() { return "SQLServerPreparedStatement"; @@ -2452,8 +2454,72 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL } checkClosed(); discardLastExecutionResults(); - + int updateCounts[]; + + localUserSQL = userSQL; + + try { + if (isInsert(localUserSQL) && connection.isAzureDW()) { + if (batchParamValues == null) { + updateCounts = new int[0]; + loggerExternal.exiting(getClassNameLogging(), "executeBatch", updateCounts); + return updateCounts; + } + + String tableName = parseUserSQLForTableNameDW(false, false); + ArrayList columnList = parseUserSQLForColumnListDW(); + ArrayList valueList = parseUserSQLForValueListDW(); + + String destinationTableName = tableName; + // Get destination metadata + try (SQLServerResultSet rs = ((SQLServerStatement) connection.createStatement()) + .executeQueryInternal("SET FMTONLY ON SELECT * FROM " + destinationTableName + " SET FMTONLY OFF ");) { + + SQLServerBulkBatchInsertRecord batchRecord = new SQLServerBulkBatchInsertRecord(batchParamValues, columnList, valueList, null); + + for (int i = 1; i <= rs.getColumnCount(); i++) { + Column c = rs.getColumn(i); + CryptoMetadata cryptoMetadata = c.getCryptoMetadata(); + int jdbctype; + TypeInfo ti = c.getTypeInfo(); + if (null != cryptoMetadata) { + jdbctype = cryptoMetadata.getBaseTypeInfo().getSSType().getJDBCType().getIntValue(); + } else { + jdbctype = ti.getSSType().getJDBCType().getIntValue(); + } + batchRecord.addColumnMetadata(i, c.getColumnName(), jdbctype, ti.getPrecision(), ti.getScale()); + } + + SQLServerBulkCopy bcOperation = new SQLServerBulkCopy(connection); + bcOperation.setDestinationTableName(tableName); + bcOperation.writeToServer((ISQLServerBulkRecord) batchRecord); + bcOperation.close(); + updateCounts = new int[batchParamValues.size()]; + for (int i = 0; i < batchParamValues.size(); ++i) + { + updateCounts[i] = 1; + } + + batchParamValues = null; + loggerExternal.exiting(getClassNameLogging(), "executeBatch", updateCounts); + return updateCounts; + } + } + } + catch (SQLException e) { + // Unable to retrieve metadata for destination + // create an error message for failing bulk copy + insert batch + throw new SQLServerException(SQLServerException.getErrString("R_unableRetrieveColMeta"), e); + } + catch (Exception e) { + // If we fail with non-SQLException, fall back to the original batch insert logic. + if (getStatementLogger().isLoggable(java.util.logging.Level.FINE)) { + getStatementLogger().fine("Parsing user's Batch Insert SQL Query failed: " + e.toString()); + getStatementLogger().fine("Falling back to the original implementation for Batch Insert."); + } + } + if (batchParamValues == null) updateCounts = new int[0]; @@ -2554,6 +2620,301 @@ public long[] executeLargeBatch() throws SQLServerException, BatchUpdateExceptio loggerExternal.exiting(getClassNameLogging(), "executeLargeBatch", updateCounts); return updateCounts; } + + + private String parseUserSQLForTableNameDW(boolean hasInsertBeenFound, boolean hasIntoBeenFound) { + // As far as finding the table name goes, There are two cases: + // Insert into and Insert + // And there could be in-line comments (with /* and */) in between. + // This method assumes the localUserSQL string starts with "insert". + localUserSQL = localUserSQL.trim(); + if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { + int temp = localUserSQL.indexOf("*/"); + localUserSQL = localUserSQL.substring(temp); + return parseUserSQLForTableNameDW(hasInsertBeenFound, hasIntoBeenFound); + } + + if (localUserSQL.substring(0, 6).equalsIgnoreCase("insert") && !hasInsertBeenFound) { + localUserSQL = localUserSQL.substring(6); + return parseUserSQLForTableNameDW(true, hasIntoBeenFound); + } + + if (localUserSQL.substring(0, 4).equalsIgnoreCase("into") && !hasIntoBeenFound) { + // is it really "into"? + // if the "into" is followed by a blank space or /*, then yes. + if (localUserSQL.charAt(4) == ' ' || localUserSQL.charAt(4) == '\t' || + (localUserSQL.charAt(4) == '/' && localUserSQL.charAt(5) == '*')) { + localUserSQL = localUserSQL.substring(4); + return parseUserSQLForTableNameDW(hasInsertBeenFound, true); + } + + // otherwise, we found the token that either contains the databasename.tablename or tablename. + // Recursively handle this, but into has been found. (or rather, it's absent in the query - the "into" keyword is optional) + return parseUserSQLForTableNameDW(hasInsertBeenFound, true); + } + + // At this point, the next token has to be the table name. + // It could be encapsulated in [], "", or have a database name preceding the table name. + // If it's encapsulated in [] or "", we need be more careful with parsing as anything could go into []/"". + // For ] or ", they can be escaped by ]] or "", watch out for this too. + if (localUserSQL.substring(0, 1).equalsIgnoreCase("[")) { + int tempint = localUserSQL.indexOf("]", 1); + + // keep checking if it's escaped + while (localUserSQL.charAt(tempint + 1) == ']') { + localUserSQL = localUserSQL.substring(0, tempint) + localUserSQL.substring(tempint + 1); + tempint = localUserSQL.indexOf("]", tempint + 1); + } + + // we've found a ] that is actually trying to close the square bracket. + // If it's followed by a dot, then it's a database. + // Otherwise, it's the table. + if (localUserSQL.charAt(tempint + 1) == '.') { + String tempstr = localUserSQL.substring(1, tempint); + localUserSQL = localUserSQL.substring(tempint + 2); + return tempstr + "." + parseUserSQLForTableNameDW(hasInsertBeenFound, hasIntoBeenFound); + } else { + // return tablename + String tempstr = localUserSQL.substring(1, tempint); + localUserSQL = localUserSQL.substring(tempint + 1); + return tempstr; + } + } + + // do the same for "" + if (localUserSQL.substring(0, 1).equalsIgnoreCase("\"")) { + int tempint = localUserSQL.indexOf("\"", 1); + + // keep checking if it's escaped + while (localUserSQL.charAt(tempint + 1) == '\"') { + localUserSQL = localUserSQL.substring(0, tempint) + localUserSQL.substring(tempint + 1); + tempint = localUserSQL.indexOf("\"", tempint + 1); + } + + // we've found a " that is actually trying to close the quote. + // If it's followed by a dot, then it's a database. + // Otherwise, it's the table. + if (localUserSQL.charAt(tempint + 1) == '.') { + String tempstr = localUserSQL.substring(1, tempint); + localUserSQL = localUserSQL.substring(tempint + 2); + return tempstr + "." + parseUserSQLForTableNameDW(hasInsertBeenFound, hasIntoBeenFound); + } else { + // return tablename + String tempstr = localUserSQL.substring(1, tempint); + localUserSQL = localUserSQL.substring(tempint + 1); + return tempstr; + } + } + + // At this point, the next chunk of string is the table name (could have database name), without starting with [ or ". + StringBuilder sb = new StringBuilder(); + while (localUserSQL.length() > 0) { + if (localUserSQL.charAt(0) == '.' || localUserSQL.charAt(0) == ' ' || localUserSQL.charAt(0) == '\t' + || localUserSQL.charAt(0) == '(') { + if (localUserSQL.charAt(0) == '.') { + localUserSQL = localUserSQL.substring(1); + return sb.toString() + "." + parseUserSQLForTableNameDW(hasInsertBeenFound, hasIntoBeenFound); + } else { + return sb.toString(); + } + } else { + sb.append(localUserSQL.charAt(0)); + localUserSQL = localUserSQL.substring(1); + } + } + + // It shouldn't come here. If we did, something is wrong. + throw new IllegalArgumentException("localUserSQL"); + } + + private ArrayList parseUserSQLForColumnListDW() { + localUserSQL = localUserSQL.trim(); + + // ignore all comments + if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { + int temp = localUserSQL.indexOf("*/"); + localUserSQL = localUserSQL.substring(temp); + return parseUserSQLForColumnListDW(); + } + + //check if optional column list was provided + // Columns can have the form of c1, [c1] or "c1". It can escape ] or " by ]] or "". + if (localUserSQL.substring(0, 1).equalsIgnoreCase("(")) { + localUserSQL = localUserSQL.substring(1); + return parseUserSQLForColumnListDWHelper(new ArrayList()); + } + return null; + } + + private ArrayList parseUserSQLForColumnListDWHelper(ArrayList listOfColumns) { + localUserSQL = localUserSQL.trim(); + + // ignore all comments + if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { + int temp = localUserSQL.indexOf("*/"); + localUserSQL = localUserSQL.substring(temp); + return parseUserSQLForColumnListDWHelper(listOfColumns); + } + + if (localUserSQL.charAt(0) == ')') { + localUserSQL = localUserSQL.substring(1); + return listOfColumns; + } + + if (localUserSQL.charAt(0) == ',') { + localUserSQL = localUserSQL.substring(1); + return parseUserSQLForColumnListDWHelper(listOfColumns); + } + + if (localUserSQL.charAt(0) == '[') { + int tempint = localUserSQL.indexOf("]", 1); + + // keep checking if it's escaped + while (localUserSQL.charAt(tempint + 1) == ']') { + localUserSQL = localUserSQL.substring(0, tempint) + localUserSQL.substring(tempint + 1); + tempint = localUserSQL.indexOf("]", tempint + 1); + } + + // we've found a ] that is actually trying to close the square bracket. + String tempstr = localUserSQL.substring(1, tempint); + localUserSQL = localUserSQL.substring(tempint + 1); + listOfColumns.add(tempstr); + return parseUserSQLForColumnListDWHelper(listOfColumns); + } + + if (localUserSQL.charAt(0) == '\"') { + int tempint = localUserSQL.indexOf("\"", 1); + + // keep checking if it's escaped + while (localUserSQL.charAt(tempint + 1) == '\"') { + localUserSQL = localUserSQL.substring(0, tempint) + localUserSQL.substring(tempint + 1); + tempint = localUserSQL.indexOf("\"", tempint + 1); + } + + // we've found a " that is actually trying to close the quote. + String tempstr = localUserSQL.substring(1, tempint); + localUserSQL = localUserSQL.substring(tempint + 1); + listOfColumns.add(tempstr); + return parseUserSQLForColumnListDWHelper(listOfColumns); + } + + // At this point, the next chunk of string is the column name, without starting with [ or ". + StringBuilder sb = new StringBuilder(); + while (localUserSQL.length() > 0) { + if (localUserSQL.charAt(0) == ',' || localUserSQL.charAt(0) == ')') { + if (localUserSQL.charAt(0) == ',') { + localUserSQL = localUserSQL.substring(1); + listOfColumns.add(sb.toString()); + return parseUserSQLForColumnListDWHelper(listOfColumns); + } else { + localUserSQL = localUserSQL.substring(1); + listOfColumns.add(sb.toString()); + return listOfColumns; + } + } else { + sb.append(localUserSQL.charAt(0)); + localUserSQL = localUserSQL.substring(1); + } + } + + // It shouldn't come here. If we did, something is wrong. + throw new IllegalArgumentException("localUserSQL"); + } + + + private ArrayList parseUserSQLForValueListDW() { + localUserSQL = localUserSQL.trim(); + + // ignore all comments + if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { + int temp = localUserSQL.indexOf("*/"); + localUserSQL = localUserSQL.substring(temp); + return parseUserSQLForColumnListDW(); + } + + // look for keyword "VALUES" + if (localUserSQL.substring(0, 6).equalsIgnoreCase("VALUES")) { + localUserSQL = localUserSQL.substring(6); + + localUserSQL = localUserSQL.trim(); + + // ignore all comments + if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { + int temp = localUserSQL.indexOf("*/"); + localUserSQL = localUserSQL.substring(temp); + return parseUserSQLForColumnListDW(); + } + + if (localUserSQL.substring(0, 1).equalsIgnoreCase("(")) { + localUserSQL = localUserSQL.substring(1); + return parseUserSQLForValueListDWHelper(new ArrayList()); + } + } + + // shouldn't come here, as the list of values is mandatory. + throw new IllegalArgumentException("localUserSQL"); + } + + private ArrayList parseUserSQLForValueListDWHelper(ArrayList listOfValues) { + localUserSQL = localUserSQL.trim(); + + // ignore all comments + if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { + int temp = localUserSQL.indexOf("*/"); + localUserSQL = localUserSQL.substring(temp); + return parseUserSQLForColumnListDWHelper(listOfValues); + } + + + if (localUserSQL.charAt(0) == ')') { + localUserSQL = localUserSQL.substring(1); + return listOfValues; + } + + if (localUserSQL.charAt(0) == ',') { + localUserSQL = localUserSQL.substring(1); + return parseUserSQLForColumnListDWHelper(listOfValues); + } + + if (localUserSQL.charAt(0) == '\'') { + int tempint = localUserSQL.indexOf("\'", 1); + + // keep checking if it's escaped + while (localUserSQL.charAt(tempint + 1) == '\'') { + localUserSQL = localUserSQL.substring(0, tempint) + localUserSQL.substring(tempint + 1); + tempint = localUserSQL.indexOf("\'", tempint + 1); + } + + // we've found a ' that is actually trying to close the quote. + // Include 's around the string as well, so we can distinguish '?' and ? later on. + String tempstr = localUserSQL.substring(0, tempint + 1); + localUserSQL = localUserSQL.substring(tempint + 1); + listOfValues.add(tempstr); + return parseUserSQLForColumnListDWHelper(listOfValues); + } + + // At this point, the next chunk of string is the value, without starting with ' (most likely a ?). + StringBuilder sb = new StringBuilder(); + while (localUserSQL.length() > 0) { + if (localUserSQL.charAt(0) == ',' || localUserSQL.charAt(0) == ')') { + if (localUserSQL.charAt(0) == ',') { + localUserSQL = localUserSQL.substring(1); + listOfValues.add(sb.toString()); + return parseUserSQLForColumnListDWHelper(listOfValues); + } else { + localUserSQL = localUserSQL.substring(1); + listOfValues.add(sb.toString()); + return listOfValues; + } + } else { + sb.append(localUserSQL.charAt(0)); + localUserSQL = localUserSQL.substring(1); + } + } + + // It shouldn't come here. If we did, something is wrong. + throw new IllegalArgumentException("localUserSQL"); + } private final class PrepStmtBatchExecCmd extends TDSCommand { private final SQLServerPreparedStatement stmt; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index 512363b4a..60bfbd650 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -963,6 +963,28 @@ final void resetForReexecute() throws SQLServerException { return false; return temp.substring(0, 6).equalsIgnoreCase("select"); } + + /** + * Determine if the SQL is a INSERT. + * + * @param sql + * The statment SQL. + * @return True is the statement is a select. + */ + /* L0 */ final boolean isInsert(String sql) throws SQLServerException { + checkClosed(); + // Used to check just the first letter which would cause + // "Set" commands to return true... + String temp = sql.trim(); + if (temp.substring(0, 2).equalsIgnoreCase("/*")) { + int index = temp.indexOf("*/"); + return isInsert(temp.substring(index)); + } + char c = temp.charAt(0); + if (c != 'i' && c != 'I') + return false; + return temp.substring(0, 6).equalsIgnoreCase("insert"); + } /** * Replace a JDBC parameter marker with the parameter's string value From 52da9aa74a0929a24fe7c5e8f954f3d179b42b23 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Fri, 27 Apr 2018 14:02:08 -0700 Subject: [PATCH 14/84] Parse bug fixing and test added --- .../jdbc/SQLServerPreparedStatement.java | 62 ++++--- .../sqlserver/jdbc/SQLServerStatement.java | 2 +- .../BatchExecutionWithBulkCopyParseTest.java | 168 ++++++++++++++++++ 3 files changed, 211 insertions(+), 21 deletions(-) create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyParseTest.java diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index a2ece7137..32b8595ac 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -2469,7 +2469,7 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL String tableName = parseUserSQLForTableNameDW(false, false); ArrayList columnList = parseUserSQLForColumnListDW(); - ArrayList valueList = parseUserSQLForValueListDW(); + ArrayList valueList = parseUserSQLForValueListDW(false); String destinationTableName = tableName; // Get destination metadata @@ -2629,7 +2629,7 @@ private String parseUserSQLForTableNameDW(boolean hasInsertBeenFound, boolean ha // This method assumes the localUserSQL string starts with "insert". localUserSQL = localUserSQL.trim(); if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { - int temp = localUserSQL.indexOf("*/"); + int temp = localUserSQL.indexOf("*/") + 2; localUserSQL = localUserSQL.substring(temp); return parseUserSQLForTableNameDW(hasInsertBeenFound, hasIntoBeenFound); } @@ -2732,7 +2732,7 @@ private ArrayList parseUserSQLForColumnListDW() { // ignore all comments if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { - int temp = localUserSQL.indexOf("*/"); + int temp = localUserSQL.indexOf("*/") + 2; localUserSQL = localUserSQL.substring(temp); return parseUserSQLForColumnListDW(); } @@ -2751,7 +2751,7 @@ private ArrayList parseUserSQLForColumnListDWHelper(ArrayList li // ignore all comments if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { - int temp = localUserSQL.indexOf("*/"); + int temp = localUserSQL.indexOf("*/") + 2; localUserSQL = localUserSQL.substring(temp); return parseUserSQLForColumnListDWHelper(listOfColumns); } @@ -2811,6 +2811,10 @@ private ArrayList parseUserSQLForColumnListDWHelper(ArrayList li listOfColumns.add(sb.toString()); return listOfColumns; } + } else if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { + int temp = localUserSQL.indexOf("*/") + 2; + localUserSQL = localUserSQL.substring(temp); + localUserSQL = localUserSQL.trim(); } else { sb.append(localUserSQL.charAt(0)); localUserSQL = localUserSQL.substring(1); @@ -2822,27 +2826,41 @@ private ArrayList parseUserSQLForColumnListDWHelper(ArrayList li } - private ArrayList parseUserSQLForValueListDW() { + private ArrayList parseUserSQLForValueListDW(boolean hasValuesBeenFound) { localUserSQL = localUserSQL.trim(); // ignore all comments if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { - int temp = localUserSQL.indexOf("*/"); + int temp = localUserSQL.indexOf("*/") + 2; localUserSQL = localUserSQL.substring(temp); - return parseUserSQLForColumnListDW(); + return parseUserSQLForValueListDW(hasValuesBeenFound); } - // look for keyword "VALUES" - if (localUserSQL.substring(0, 6).equalsIgnoreCase("VALUES")) { - localUserSQL = localUserSQL.substring(6); - - localUserSQL = localUserSQL.trim(); - + if (!hasValuesBeenFound) { + // look for keyword "VALUES" + if (localUserSQL.substring(0, 6).equalsIgnoreCase("VALUES")) { + localUserSQL = localUserSQL.substring(6); + + localUserSQL = localUserSQL.trim(); + + // ignore all comments + if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { + int temp = localUserSQL.indexOf("*/") + 2; + localUserSQL = localUserSQL.substring(temp); + return parseUserSQLForValueListDW(true); + } + + if (localUserSQL.substring(0, 1).equalsIgnoreCase("(")) { + localUserSQL = localUserSQL.substring(1); + return parseUserSQLForValueListDWHelper(new ArrayList()); + } + } + } else { // ignore all comments if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { - int temp = localUserSQL.indexOf("*/"); + int temp = localUserSQL.indexOf("*/") + 2; localUserSQL = localUserSQL.substring(temp); - return parseUserSQLForColumnListDW(); + return parseUserSQLForValueListDW(hasValuesBeenFound); } if (localUserSQL.substring(0, 1).equalsIgnoreCase("(")) { @@ -2860,9 +2878,9 @@ private ArrayList parseUserSQLForValueListDWHelper(ArrayList lis // ignore all comments if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { - int temp = localUserSQL.indexOf("*/"); + int temp = localUserSQL.indexOf("*/") + 2; localUserSQL = localUserSQL.substring(temp); - return parseUserSQLForColumnListDWHelper(listOfValues); + return parseUserSQLForValueListDWHelper(listOfValues); } @@ -2873,7 +2891,7 @@ private ArrayList parseUserSQLForValueListDWHelper(ArrayList lis if (localUserSQL.charAt(0) == ',') { localUserSQL = localUserSQL.substring(1); - return parseUserSQLForColumnListDWHelper(listOfValues); + return parseUserSQLForValueListDWHelper(listOfValues); } if (localUserSQL.charAt(0) == '\'') { @@ -2890,7 +2908,7 @@ private ArrayList parseUserSQLForValueListDWHelper(ArrayList lis String tempstr = localUserSQL.substring(0, tempint + 1); localUserSQL = localUserSQL.substring(tempint + 1); listOfValues.add(tempstr); - return parseUserSQLForColumnListDWHelper(listOfValues); + return parseUserSQLForValueListDWHelper(listOfValues); } // At this point, the next chunk of string is the value, without starting with ' (most likely a ?). @@ -2900,12 +2918,16 @@ private ArrayList parseUserSQLForValueListDWHelper(ArrayList lis if (localUserSQL.charAt(0) == ',') { localUserSQL = localUserSQL.substring(1); listOfValues.add(sb.toString()); - return parseUserSQLForColumnListDWHelper(listOfValues); + return parseUserSQLForValueListDWHelper(listOfValues); } else { localUserSQL = localUserSQL.substring(1); listOfValues.add(sb.toString()); return listOfValues; } + } else if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { + int temp = localUserSQL.indexOf("*/") + 2; + localUserSQL = localUserSQL.substring(temp); + localUserSQL = localUserSQL.trim(); } else { sb.append(localUserSQL.charAt(0)); localUserSQL = localUserSQL.substring(1); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index 60bfbd650..55dcc2420 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -977,7 +977,7 @@ final void resetForReexecute() throws SQLServerException { // "Set" commands to return true... String temp = sql.trim(); if (temp.substring(0, 2).equalsIgnoreCase("/*")) { - int index = temp.indexOf("*/"); + int index = temp.indexOf("*/") + 2; return isInsert(temp.substring(index)); } char c = temp.charAt(0); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyParseTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyParseTest.java new file mode 100644 index 000000000..79a43c5e6 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyParseTest.java @@ -0,0 +1,168 @@ +package com.microsoft.sqlserver.jdbc.preparedStatement; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; +import org.opentest4j.TestAbortedException; + +import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; +import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.DBConnection; +import com.microsoft.sqlserver.testframework.Utils; + + +@RunWith(JUnitPlatform.class) +public class BatchExecutionWithBulkCopyParseTest extends AbstractTest { + + static SQLServerPreparedStatement pstmt = null; + static Statement stmt = null; + static Connection connection = null; + + @Test + public void testIsInsert() throws SQLException, NoSuchMethodException, SecurityException, + IllegalAccessException, IllegalArgumentException, InvocationTargetException { + String valid1 = "INSERT INTO PeterTable values (1, 2)"; + String valid2 = " INSERT INTO PeterTable values (1, 2)"; + String valid3 = "/* asdf */ INSERT INTO PeterTable values (1, 2)"; + String invalid = "Select * from PEterTable"; + + stmt = connection.createStatement(); + Method method = stmt.getClass().getDeclaredMethod("isInsert", String.class); + method.setAccessible(true); + assertTrue((boolean) method.invoke(stmt, valid1)); + assertTrue((boolean) method.invoke(stmt, valid2)); + assertTrue((boolean) method.invoke(stmt, valid3)); + assertFalse((boolean) method.invoke(stmt, invalid)); + } + + @Test + public void testComments() throws SQLException, NoSuchFieldException, SecurityException, + IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + pstmt = (SQLServerPreparedStatement) connection.prepareStatement(""); + + String valid = "/* rando comment *//* rando comment */ INSERT /* rando comment */ INTO /* rando comment *//*rando comment*/ PeterTable /*rando comment */" + + " /* rando comment */values/* rando comment */ (1, 2)"; + + Field f1 = pstmt.getClass().getSuperclass().getDeclaredField("localUserSQL"); + f1.setAccessible(true); + f1.set(pstmt, valid); + + Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class); + method.setAccessible(true); + + assertEquals((String) method.invoke(pstmt, false, false), "PeterTable"); + } + + @Test + public void testBrackets() throws SQLException, NoSuchFieldException, SecurityException, + IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + pstmt = (SQLServerPreparedStatement) connection.prepareStatement(""); + + String valid = "/* rando comment *//* rando comment */ INSERT /* rando comment */ INTO /* rando comment *//*rando comment*/ [Peter[]]Table] /*rando comment */" + + " /* rando comment */values/* rando comment */ (1, 2)"; + + Field f1 = pstmt.getClass().getSuperclass().getDeclaredField("localUserSQL"); + f1.setAccessible(true); + f1.set(pstmt, valid); + + Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class); + method.setAccessible(true); + + assertEquals((String) method.invoke(pstmt, false, false), "Peter[]Table"); + } + + @Test + public void testDoubleQuotes() throws SQLException, NoSuchFieldException, SecurityException, + IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + pstmt = (SQLServerPreparedStatement) connection.prepareStatement(""); + + String valid = "/* rando comment *//* rando comment */ INSERT /* rando comment */ INTO /* rando comment *//*rando comment*/ \"Peter\"\"\"\"Table\" /*rando comment */" + + " /* rando comment */values/* rando comment */ (1, 2)"; + + Field f1 = pstmt.getClass().getSuperclass().getDeclaredField("localUserSQL"); + f1.setAccessible(true); + f1.set(pstmt, valid); + + Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class); + method.setAccessible(true); + + assertEquals((String) method.invoke(pstmt, false, false), "Peter\"\"Table"); + } + + @Test + public void testAll() throws SQLException, NoSuchFieldException, SecurityException, + IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + pstmt = (SQLServerPreparedStatement) connection.prepareStatement(""); + + String valid = "/* rando comment *//* rando comment */ INSERT /* rando comment */ INTO /* rando comment *//*rando comment*/ \"Peter\"\"\"\"Table\" /*rando comment */" + + " /* rando comment */ (\"c1\"/* rando comment */, /* rando comment */[c2]/* rando comment */, /* rando comment */ /* rando comment */c3/* rando comment */, c4)" + + "values/* rando comment */ (/* rando comment */1/* rando comment */, /* rando comment */2/* rando comment */ , '?', ?)/* rando comment */"; + + Field f1 = pstmt.getClass().getSuperclass().getDeclaredField("localUserSQL"); + f1.setAccessible(true); + f1.set(pstmt, valid); + + Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class); + method.setAccessible(true); + + assertEquals((String) method.invoke(pstmt, false, false), "Peter\"\"Table"); + + method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForColumnListDW"); + method.setAccessible(true); + + ArrayList columnList = (ArrayList) method.invoke(pstmt); + ArrayList columnListExpected = new ArrayList(); + columnListExpected.add("c1"); + columnListExpected.add("c2"); + columnListExpected.add("c3"); + columnListExpected.add("c4"); + + for (int i = 0; i < columnListExpected.size(); i++) { + assertEquals(columnList.get(i), columnListExpected.get(i)); + } + + method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForValueListDW", boolean.class); + method.setAccessible(true); + + ArrayList valueList = (ArrayList) method.invoke(pstmt, false); + ArrayList valueListExpected = new ArrayList(); + valueListExpected.add("1"); + valueListExpected.add("2"); + valueListExpected.add("'?'"); + valueListExpected.add("?"); + + for (int i = 0; i < valueListExpected.size(); i++) { + assertEquals(valueList.get(i), valueListExpected.get(i)); + } + } + + @BeforeEach + public void testSetup() throws TestAbortedException, Exception { + assumeTrue(13 <= new DBConnection(connectionString).getServerVersion(), + "Aborting test case as SQL Server version is not compatible with Always encrypted "); + + connection = DriverManager.getConnection(connectionString); + SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); + Utils.dropTableIfExists("esimple", stmt); + String sql1 = "create table esimple (id integer not null, name varchar(255), constraint pk_esimple primary key (id))"; + stmt.execute(sql1); + stmt.close(); + } +} From 5bb79a23fcf525b2a8b54f67f6b979bca9b4d6da Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Mon, 30 Apr 2018 17:15:52 -0700 Subject: [PATCH 15/84] bug fix + additional tests --- .../jdbc/SQLServerBulkBatchInsertRecord.java | 9 +- .../preparedStatement/RegressionTest.java | 109 +++++ .../statement/BatchExecuteWithErrorsTest.java | 425 ++++++++++++++++++ .../unit/statement/BatchExecutionTest.java | 117 +++++ .../unit/statement/PreparedStatementTest.java | 178 ++++++++ 5 files changed, 833 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java index f987ad821..3b570cd57 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java @@ -75,14 +75,14 @@ private class ColumnMetadata { * Class name for logging. */ private static final String loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkBatchInsertRecord"; - + /* * Logger */ private static final java.util.logging.Logger loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); private ArrayList batchParam; - private int batchParamIndex = 0; + private int batchParamIndex = -1; private ArrayList columnList; private ArrayList valueList; @@ -352,7 +352,7 @@ public Object[] getRowData() throws SQLServerException { int valueIndex = 0; for (int i = 0; i < data.length; i++) { if (valueList.get(i).equalsIgnoreCase("?")) { - data[i] = batchParam.get(i)[valueIndex].getSetterValue(); + data[i] = batchParam.get(batchParamIndex)[valueIndex].getSetterValue(); valueIndex++; } else { // remove 's at the beginning and end of the value, if it exists. @@ -388,8 +388,6 @@ public Object[] getRowData() throws SQLServerException { } } - batchParamIndex++; - // Cannot go directly from String[] to Object[] and expect it to act as an array. Object[] dataRow = new Object[data.length]; @@ -565,6 +563,7 @@ else if (dateTimeFormatter != null) @Override public boolean next() throws SQLServerException { + batchParamIndex++; return batchParamIndex < batchParam.size(); } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java index 05f36bdee..4bbf8cb8d 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java @@ -9,6 +9,7 @@ import static org.junit.jupiter.api.Assertions.fail; +import java.lang.reflect.Field; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; @@ -312,7 +313,115 @@ public void batchWithLargeStringTest() throws SQLException { } } + + @Test + public void batchWithLargeStringTestUseBulkCopyAPI() throws SQLException { + Statement stmt = con.createStatement(); + PreparedStatement pstmt = null; + ResultSet rs = null; + String testTable = "TEST_TABLE_BULK_COPY"; + Utils.dropTableIfExists(testTable, stmt); + + con.setAutoCommit(false); + + // create a table with two columns + boolean createPrimaryKey = false; + try { + stmt.execute("if object_id('" + testTable + "', 'U') is not null\ndrop table " + testTable + ";"); + if (createPrimaryKey) { + stmt.execute("create table " + testTable + " ( ID int, DATA nvarchar(max), primary key (ID) );"); + } + else { + stmt.execute("create table " + testTable + " ( ID int, DATA nvarchar(max) );"); + } + } + catch (Exception e) { + fail(e.toString()); + } + con.commit(); + + // build a String with 4001 characters + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < 4001; i++) { + stringBuilder.append('c'); + } + String largeString = stringBuilder.toString(); + + String[] values = {"a", "b", largeString, "d", "e"}; + // insert five rows into the table; use a batch for each row + try { + Field f1 = con.getClass().getSuperclass().getDeclaredField("isAzureDW"); + f1.setAccessible(true); + f1.set(con, true); + pstmt = con.prepareStatement("insert into " + testTable + " values (?,?)"); + // 0,a + pstmt.setInt(1, 0); + pstmt.setNString(2, values[0]); + pstmt.addBatch(); + + // 1,b + pstmt.setInt(1, 1); + pstmt.setNString(2, values[1]); + pstmt.addBatch(); + + // 2,ccc... + pstmt.setInt(1, 2); + pstmt.setNString(2, values[2]); + pstmt.addBatch(); + + // 3,d + pstmt.setInt(1, 3); + pstmt.setNString(2, values[3]); + pstmt.addBatch(); + + // 4,e + pstmt.setInt(1, 4); + pstmt.setNString(2, values[4]); + pstmt.addBatch(); + + pstmt.executeBatch(); + } + catch (Exception e) { + fail(e.toString()); + } + + connection.commit(); + + // check the data in the table + Map selectedValues = new LinkedHashMap<>(); + int id = 0; + try { + pstmt = con.prepareStatement("select * from " + testTable + ";"); + try { + rs = pstmt.executeQuery(); + int i = 0; + while (rs.next()) { + id = rs.getInt(1); + String data = rs.getNString(2); + if (selectedValues.containsKey(id)) { + fail("Found duplicate id: " + id + " ,actual values is : " + values[i++] + " data is: " + data); + } + selectedValues.put(id, data); + } + } + finally { + if (null != rs) { + rs.close(); + } + } + } + finally { + Utils.dropTableIfExists(testTable, stmt); + if (null != pstmt) { + pstmt.close(); + } + if (null != stmt) { + stmt.close(); + } + } + + } /** * Test with large string and tests with more batch queries * diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java index 38c16ce95..facdeb0b3 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java @@ -14,6 +14,7 @@ import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; +import java.lang.reflect.Field; import java.sql.BatchUpdateException; import java.sql.Connection; import java.sql.DriverManager; @@ -263,6 +264,226 @@ public void Repro47239() throws SQLException { conn.close(); } + /** + * Batch test + * + * @throws SQLException + * @throws SecurityException + * @throws NoSuchFieldException + * @throws IllegalAccessException + * @throws IllegalArgumentException + */ + @Test + @DisplayName("Batch Test") + public void Repro47239UseBulkCopyAPI() throws SQLException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + String tableN = RandomUtil.getIdentifier("t_Repro47239"); + final String tableName = AbstractSQLGenerator.escapeIdentifier(tableN); + final String insertStmt = "INSERT INTO " + tableName + " VALUES (999, 'HELLO', '4/12/1994')"; + final String error16 = "RAISERROR ('raiserror level 16',16,42)"; + final String select = "SELECT 1"; + final String dateConversionError = "insert into " + tableName + " values (999999, 'Hello again', 'asdfasdf')"; + + String warning; + String error; + String severe; + con = DriverManager.getConnection(connectionString); + Field f1 = con.getClass().getSuperclass().getDeclaredField("isAzureDW"); + f1.setAccessible(true); + f1.set(con, true); + if (DBConnection.isSqlAzure(con)) { + // SQL Azure will throw exception for "raiserror WITH LOG", so the following RAISERROR statements have not "with log" option + warning = "RAISERROR ('raiserror level 4',4,1)"; + error = "RAISERROR ('raiserror level 11',11,1)"; + // On SQL Azure, raising FATAL error by RAISERROR() is not supported and there is no way to + // cut the current connection by a statement inside a SQL batch. + // Details: Although one can simulate a fatal error (that cuts the connections) by dropping the database, + // this simulation cannot be written entirely in TSQL (because it needs a new connection), + // and thus it cannot be put into a TSQL batch and it is useless here. + // So we have to skip the last scenario of this test case, i.e. "Test Severe (connection-closing) errors" + // It is worthwhile to still execute the first 5 test scenarios of this test case, in order to have best test coverage. + severe = "--Not executed when testing against SQL Azure"; // this is a dummy statement that never being executed on SQL Azure + } + else { + warning = "RAISERROR ('raiserror level 4',4,1) WITH LOG"; + error = "RAISERROR ('raiserror level 11',11,1) WITH LOG"; + severe = "RAISERROR ('raiserror level 20',20,1) WITH LOG"; + } + con.close(); + + int[] actualUpdateCounts; + int[] expectedUpdateCounts; + String actualExceptionText; + + // SQL Server 2005 driver + try { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + } + catch (ClassNotFoundException e1) { + fail(e1.toString()); + } + Connection conn = DriverManager.getConnection(connectionString); + Statement stmt = conn.createStatement(); + try { + stmt.executeUpdate("drop table " + tableName); + } + catch (Exception ignored) { + } + stmt.executeUpdate( + "create table " + tableName + " (c1_int int, c2_varchar varchar(20), c3_date datetime, c4_int int identity(1,1) primary key)"); + + // Regular Statement batch update + expectedUpdateCounts = new int[] {1, -2, 1, -2, 1, -2}; + Statement batchStmt = conn.createStatement(); + batchStmt.addBatch(insertStmt); + batchStmt.addBatch(warning); + batchStmt.addBatch(insertStmt); + batchStmt.addBatch(warning); + batchStmt.addBatch(insertStmt); + batchStmt.addBatch(warning); + try { + actualUpdateCounts = batchStmt.executeBatch(); + actualExceptionText = ""; + } + catch (BatchUpdateException bue) { + actualUpdateCounts = bue.getUpdateCounts(); + actualExceptionText = bue.getMessage(); + if (log.isLoggable(Level.FINE)) { + log.fine("BatchUpdateException occurred. Message:" + actualExceptionText); + } + } + finally { + batchStmt.close(); + } + if (log.isLoggable(Level.FINE)) { + log.fine("UpdateCounts:"); + } + for (int updateCount : actualUpdateCounts) { + log.fine("" + updateCount + ","); + } + log.fine(""); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test interleaved inserts and warnings"); + + expectedUpdateCounts = new int[] {-3, 1, 1, 1}; + stmt.addBatch(error); + stmt.addBatch(insertStmt); + stmt.addBatch(insertStmt); + stmt.addBatch(insertStmt); + try { + actualUpdateCounts = stmt.executeBatch(); + actualExceptionText = ""; + } + catch (BatchUpdateException bue) { + actualUpdateCounts = bue.getUpdateCounts(); + actualExceptionText = bue.getMessage(); + } + log.fine("UpdateCounts:"); + for (int updateCount : actualUpdateCounts) { + log.fine("" + updateCount + ","); + } + log.fine(""); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test error followed by inserts"); + // 50280 + expectedUpdateCounts = new int[] {1, -3}; + stmt.addBatch(insertStmt); + stmt.addBatch(error16); + try { + actualUpdateCounts = stmt.executeBatch(); + actualExceptionText = ""; + } + catch (BatchUpdateException bue) { + actualUpdateCounts = bue.getUpdateCounts(); + actualExceptionText = bue.getMessage(); + } + for (int updateCount : actualUpdateCounts) { + log.fine("" + updateCount + ","); + } + log.fine(""); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test insert followed by non-fatal error (50280)"); + + // Test "soft" errors + conn.setAutoCommit(false); + stmt.addBatch(select); + stmt.addBatch(insertStmt); + stmt.addBatch(select); + stmt.addBatch(insertStmt); + try { + stmt.executeBatch(); + assertEquals(true, false, "Soft error test: executeBatch unexpectedly succeeded"); + } + catch (BatchUpdateException bue) { + assertEquals("A result set was generated for update.", bue.getMessage(), "Soft error test: wrong error message in BatchUpdateException"); + assertEquals(Arrays.equals(bue.getUpdateCounts(), new int[] {-3, 1, -3, 1}), true, + "Soft error test: wrong update counts in BatchUpdateException"); + } + conn.rollback(); + + // Defect 128801: Rollback (with conversion error) should throw SQLException + stmt.addBatch(dateConversionError); + stmt.addBatch(insertStmt); + stmt.addBatch(insertStmt); + stmt.addBatch(insertStmt); + try { + stmt.executeBatch(); + } + catch (BatchUpdateException bue) { + assertThat(bue.getMessage(), containsString("Syntax error converting date")); + // CTestLog.CompareStartsWith(bue.getMessage(), "Syntax error converting date", "Transaction rollback with conversion error threw wrong + // BatchUpdateException"); + } + catch (SQLException e) { + assertThat(e.getMessage(), containsString("Conversion failed when converting date")); + // CTestLog.CompareStartsWith(e.getMessage(), "Conversion failed when converting date", "Transaction rollback with conversion error threw + // wrong SQLException"); + } + + conn.setAutoCommit(true); + + // On SQL Azure, raising FATAL error by RAISERROR() is not supported and there is no way to + // cut the current connection by a statement inside a SQL batch. + // Details: Although one can simulate a fatal error (that cuts the connections) by dropping the database, + // this simulation cannot be written entirely in TSQL (because it needs a new connection), + // and thus it cannot be put into a TSQL batch and it is useless here. + // So we have to skip the last scenario of this test case, i.e. "Test Severe (connection-closing) errors" + // It is worthwhile to still execute the first 5 test scenarios of this test case, in order to have best test coverage. + if (!DBConnection.isSqlAzure(conn)) { + // Test Severe (connection-closing) errors + stmt.addBatch(error); + stmt.addBatch(insertStmt); + stmt.addBatch(warning); + // TODO Removed until ResultSet refactoring task (45832) is complete. + // stmt.addBatch(select); // error: select not permitted in batch + stmt.addBatch(insertStmt); + stmt.addBatch(severe); + stmt.addBatch(insertStmt); + stmt.addBatch(insertStmt); + try { + stmt.executeBatch(); + assertEquals(false, true, "Test fatal errors batch execution succeeded (should have failed)"); + } + catch (BatchUpdateException bue) { + assertEquals(false, true, "Test fatal errors returned BatchUpdateException rather than SQLException"); + } + catch (SQLException e) { + actualExceptionText = e.getMessage(); + + if (actualExceptionText.endsWith("reset")) { + assertTrue(actualExceptionText.equalsIgnoreCase("Connection reset"), "Test fatal errors"); + } + else { + assertTrue(actualExceptionText.equalsIgnoreCase("raiserror level 20"), "Test fatal errors"); + } + } + } + + try { + stmt.executeUpdate("drop table " + tableName); + } + catch (Exception ignored) { + } + stmt.close(); + conn.close(); + } + /** * Tests large methods, supported in 42 * @@ -463,4 +684,208 @@ public void Repro47239large() throws Exception { stmt.close(); conn.close(); } + + /** + * Tests large methods, supported in 42 + * + * @throws Exception + */ + @Test + @DisplayName("Regression test for using 'large' methods") + public void Repro47239largeUseBulkCopyAPI() throws Exception { + + assumeTrue("JDBC42".equals(Utils.getConfiguredProperty("JDBC_Version")), "Aborting test case as JDBC version is not compatible. "); + // the DBConnection for detecting whether the server is SQL Azure or SQL Server. + con = DriverManager.getConnection(connectionString); + Field f1 = con.getClass().getSuperclass().getDeclaredField("isAzureDW"); + f1.setAccessible(true); + f1.set(con, true); + final String warning; + final String error; + final String severe; + if (DBConnection.isSqlAzure(con)) { + // SQL Azure will throw exception for "raiserror WITH LOG", so the following RAISERROR statements have not "with log" option + warning = "RAISERROR ('raiserror level 4',4,1)"; + error = "RAISERROR ('raiserror level 11',11,1)"; + // On SQL Azure, raising FATAL error by RAISERROR() is not supported and there is no way to + // cut the current connection by a statement inside a SQL batch. + // Details: Although one can simulate a fatal error (that cuts the connections) by dropping the database, + // this simulation cannot be written entirely in TSQL (because it needs a new connection), + // and thus it cannot be put into a TSQL batch and it is useless here. + // So we have to skip the last scenario of this test case, i.e. "Test Severe (connection-closing) errors" + // It is worthwhile to still execute the first 5 test scenarios of this test case, in order to have best test coverage. + severe = "--Not executed when testing against SQL Azure"; // this is a dummy statement that never being executed on SQL Azure + } + else { + warning = "RAISERROR ('raiserror level 4',4,1) WITH LOG"; + error = "RAISERROR ('raiserror level 11',11,1) WITH LOG"; + severe = "RAISERROR ('raiserror level 20',20,1) WITH LOG"; + } + con.close(); + + long[] actualUpdateCounts; + long[] expectedUpdateCounts; + String actualExceptionText; + + // SQL Server 2005 driver + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + Connection conn = DriverManager.getConnection(connectionString); + Statement stmt = conn.createStatement(); + + try { + stmt.executeLargeUpdate("drop table " + tableName); + } + catch (Exception ignored) { + } + try { + stmt.executeLargeUpdate( + "create table " + tableName + " (c1_int int, c2_varchar varchar(20), c3_date datetime, c4_int int identity(1,1) primary key)"); + } + catch (Exception ignored) { + } + // Regular Statement batch update + expectedUpdateCounts = new long[] {1, -2, 1, -2, 1, -2}; + Statement batchStmt = conn.createStatement(); + batchStmt.addBatch(insertStmt); + batchStmt.addBatch(warning); + batchStmt.addBatch(insertStmt); + batchStmt.addBatch(warning); + batchStmt.addBatch(insertStmt); + batchStmt.addBatch(warning); + try { + actualUpdateCounts = batchStmt.executeLargeBatch(); + actualExceptionText = ""; + } + catch (BatchUpdateException bue) { + actualUpdateCounts = bue.getLargeUpdateCounts(); + actualExceptionText = bue.getMessage(); + log.fine("BatchUpdateException occurred. Message:" + actualExceptionText); + } + finally { + batchStmt.close(); + } + log.fine("UpdateCounts:"); + for (long updateCount : actualUpdateCounts) { + log.fine("" + updateCount + ","); + } + log.fine(""); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test interleaved inserts and warnings"); + + expectedUpdateCounts = new long[] {-3, 1, 1, 1}; + stmt.addBatch(error); + stmt.addBatch(insertStmt); + stmt.addBatch(insertStmt); + stmt.addBatch(insertStmt); + try { + actualUpdateCounts = stmt.executeLargeBatch(); + actualExceptionText = ""; + } + catch (BatchUpdateException bue) { + actualUpdateCounts = bue.getLargeUpdateCounts(); + actualExceptionText = bue.getMessage(); + } + log.fine("UpdateCounts:"); + for (long updateCount : actualUpdateCounts) { + log.fine("" + updateCount + ","); + } + log.fine(""); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test error followed by inserts"); + + // 50280 + expectedUpdateCounts = new long[] {1, -3}; + stmt.addBatch(insertStmt); + stmt.addBatch(error16); + try { + actualUpdateCounts = stmt.executeLargeBatch(); + actualExceptionText = ""; + } + catch (BatchUpdateException bue) { + actualUpdateCounts = bue.getLargeUpdateCounts(); + actualExceptionText = bue.getMessage(); + } + for (long updateCount : actualUpdateCounts) { + log.fine("" + updateCount + ","); + } + log.fine(""); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test insert followed by non-fatal error (50280)"); + + // Test "soft" errors + conn.setAutoCommit(false); + stmt.addBatch(select); + stmt.addBatch(insertStmt); + stmt.addBatch(select); + stmt.addBatch(insertStmt); + try { + stmt.executeLargeBatch(); + assertEquals(false, true, "Soft error test: executeLargeBatch unexpectedly succeeded"); + } + catch (BatchUpdateException bue) { + assertEquals("A result set was generated for update.", bue.getMessage(), "Soft error test: wrong error message in BatchUpdateException"); + assertEquals(Arrays.equals(bue.getLargeUpdateCounts(), new long[] {-3, 1, -3, 1}), true, + "Soft error test: wrong update counts in BatchUpdateException"); + } + conn.rollback(); + + // Defect 128801: Rollback (with conversion error) should throw SQLException + stmt.addBatch(dateConversionError); + stmt.addBatch(insertStmt); + stmt.addBatch(insertStmt); + stmt.addBatch(insertStmt); + try { + stmt.executeLargeBatch(); + } + catch (BatchUpdateException bue) { + assertThat(bue.getMessage(), containsString("Syntax error converting date")); + } + catch (SQLException e) { + assertThat(e.getMessage(), containsString("Conversion failed when converting date")); + } + + conn.setAutoCommit(true); + + // On SQL Azure, raising FATAL error by RAISERROR() is not supported and there is no way to + // cut the current connection by a statement inside a SQL batch. + // Details: Although one can simulate a fatal error (that cuts the connections) by dropping the database, + // this simulation cannot be written entirely in TSQL (because it needs a new connection), + // and thus it cannot be put into a TSQL batch and it is useless here. + // So we have to skip the last scenario of this test case, i.e. "Test Severe (connection-closing) errors" + // It is worthwhile to still execute the first 5 test scenarios of this test case, in order to have best test coverage. + if (!DBConnection.isSqlAzure(DriverManager.getConnection(connectionString))) { + // Test Severe (connection-closing) errors + stmt.addBatch(error); + stmt.addBatch(insertStmt); + stmt.addBatch(warning); + + stmt.addBatch(insertStmt); + stmt.addBatch(severe); + stmt.addBatch(insertStmt); + stmt.addBatch(insertStmt); + try { + stmt.executeLargeBatch(); + assertEquals(false, true, "Test fatal errors batch execution succeeded (should have failed)"); + } + catch (BatchUpdateException bue) { + assertEquals(false, true, "Test fatal errors returned BatchUpdateException rather than SQLException"); + } + catch (SQLException e) { + actualExceptionText = e.getMessage(); + + if (actualExceptionText.endsWith("reset")) { + assertTrue(actualExceptionText.equalsIgnoreCase("Connection reset"), "Test fatal errors"); + } + else { + assertTrue(actualExceptionText.equalsIgnoreCase("raiserror level 20"), "Test fatal errors"); + + } + } + } + + try { + stmt.executeLargeUpdate("drop table " + tableName); + } + catch (Exception ignored) { + } + stmt.close(); + conn.close(); + } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java index 85eb4a91b..70cb48fc5 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java @@ -11,6 +11,7 @@ import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; +import java.lang.reflect.Field; import java.sql.BatchUpdateException; import java.sql.Connection; import java.sql.DriverManager; @@ -53,6 +54,8 @@ public class BatchExecutionTest extends AbstractTest { public void testBatchExceptionAEOn() throws Exception { testAddBatch1(); testExecuteBatch1(); + testAddBatch1UseBulkCopyAPI(); + testExecuteBatch1UseBulkCopyAPI(); } /** @@ -110,6 +113,65 @@ public void testAddBatch1() { fail("Call to addBatch is Failed!"); } } + + /** + * Get a PreparedStatement object and call the addBatch() method with 3 SQL statements and call the executeBatch() method and it should return + * array of Integer values of length 3 + */ + public void testAddBatch1UseBulkCopyAPI() { + int i = 0; + int retValue[] = {0, 0, 0}; + try { + String sPrepStmt = "update ctstable2 set PRICE=PRICE*20 where TYPE_ID=?"; + Field f1 = connection.getClass().getSuperclass().getDeclaredField("isAzureDW"); + f1.setAccessible(true); + f1.set(connection, true); + pstmt = connection.prepareStatement(sPrepStmt); + pstmt.setInt(1, 2); + pstmt.addBatch(); + + pstmt.setInt(1, 3); + pstmt.addBatch(); + + pstmt.setInt(1, 4); + pstmt.addBatch(); + + int[] updateCount = pstmt.executeBatch(); + int updateCountlen = updateCount.length; + + assertTrue(updateCountlen == 3, "addBatch does not add the SQL Statements to Batch ,call to addBatch failed"); + + String sPrepStmt1 = "select count(*) from ctstable2 where TYPE_ID=?"; + + pstmt1 = connection.prepareStatement(sPrepStmt1); + + // 2 is the number that is set First for Type Id in Prepared Statement + for (int n = 2; n <= 4; n++) { + pstmt1.setInt(1, n); + rs = pstmt1.executeQuery(); + rs.next(); + retValue[i++] = rs.getInt(1); + } + + pstmt1.close(); + + for (int j = 0; j < updateCount.length; j++) { + + if (updateCount[j] != retValue[j] && updateCount[j] != Statement.SUCCESS_NO_INFO) { + fail("affected row count does not match with the updateCount value, Call to addBatch is Failed!"); + } + } + } + catch (BatchUpdateException b) { + fail("BatchUpdateException : Call to addBatch is Failed!"); + } + catch (SQLException sqle) { + fail("Call to addBatch is Failed!"); + } + catch (Exception e) { + fail("Call to addBatch is Failed!"); + } + } /** * Get a PreparedStatement object and call the addBatch() method with a 3 valid SQL statements and call the executeBatch() method It should return @@ -166,6 +228,61 @@ public void testExecuteBatch1() { fail("Call to executeBatch is Failed!"); } } + + public void testExecuteBatch1UseBulkCopyAPI() { + int i = 0; + int retValue[] = {0, 0, 0}; + int updCountLength = 0; + try { + String sPrepStmt = "update ctstable2 set PRICE=PRICE*20 where TYPE_ID=?"; + + pstmt = connection.prepareStatement(sPrepStmt); + Field f1 = connection.getClass().getSuperclass().getDeclaredField("isAzureDW"); + f1.setAccessible(true); + f1.set(connection, true); + pstmt.setInt(1, 1); + pstmt.addBatch(); + + pstmt.setInt(1, 2); + pstmt.addBatch(); + + pstmt.setInt(1, 3); + pstmt.addBatch(); + + int[] updateCount = pstmt.executeBatch(); + updCountLength = updateCount.length; + + assertTrue(updCountLength == 3, "executeBatch does not execute the Batch of SQL statements, Call to executeBatch is Failed!"); + + String sPrepStmt1 = "select count(*) from ctstable2 where TYPE_ID=?"; + + pstmt1 = connection.prepareStatement(sPrepStmt1); + + for (int n = 1; n <= 3; n++) { + pstmt1.setInt(1, n); + rs = pstmt1.executeQuery(); + rs.next(); + retValue[i++] = rs.getInt(1); + } + + pstmt1.close(); + + for (int j = 0; j < updateCount.length; j++) { + if (updateCount[j] != retValue[j] && updateCount[j] != Statement.SUCCESS_NO_INFO) { + fail("executeBatch does not execute the Batch of SQL statements, Call to executeBatch is Failed!"); + } + } + } + catch (BatchUpdateException b) { + fail("BatchUpdateException : Call to executeBatch is Failed!"); + } + catch (SQLException sqle) { + fail("Call to executeBatch is Failed!"); + } + catch (Exception e) { + fail("Call to executeBatch is Failed!"); + } + } private static void createTable() throws SQLException { String sql1 = "create table ctstable1 (TYPE_ID int, TYPE_DESC varchar(32), primary key(TYPE_ID)) "; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java index e4e0d09f0..c1f811709 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java @@ -14,6 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; +import java.lang.reflect.Field; import java.sql.BatchUpdateException; import java.sql.DriverManager; import java.sql.ResultSet; @@ -294,6 +295,183 @@ public void testStatementPooling() throws SQLException { } } + /** + * Test handling of statement pooling for prepared statements. + * + * @throws SQLException + * @throws SecurityException + * @throws NoSuchFieldException + * @throws IllegalAccessException + * @throws IllegalArgumentException + */ + @Test + @Tag("slow") + public void testStatementPoolingUseBulkCopyAPI() throws SQLException, NoSuchFieldException, SecurityException, + IllegalArgumentException, IllegalAccessException { + // Test % handle re-use + try (SQLServerConnection con = (SQLServerConnection)DriverManager.getConnection(connectionString)) { + Field f1 = con.getClass().getSuperclass().getDeclaredField("isAzureDW"); + f1.setAccessible(true); + f1.set(con, true); + String query = String.format("/*statementpoolingtest_re-use_%s*/SELECT TOP(1) * FROM sys.tables;", UUID.randomUUID().toString()); + + con.setStatementPoolingCacheSize(10); + + boolean[] prepOnFirstCalls = {false, true}; + + for(boolean prepOnFirstCall : prepOnFirstCalls) { + + con.setEnablePrepareOnFirstPreparedStatementCall(prepOnFirstCall); + + int[] queryCounts = {10, 20, 30, 40}; + for(int queryCount : queryCounts) { + String[] queries = new String[queryCount]; + for(int i = 0; i < queries.length; ++i) { + queries[i] = String.format("%s--%s--%s--%s", query, i, queryCount, prepOnFirstCall); + } + + int testsWithHandleReuse = 0; + final int testCount = 500; + for(int i = 0; i < testCount; ++i) { + Random random = new Random(); + int queryNumber = random.nextInt(queries.length); + try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con.prepareStatement(queries[queryNumber])) { + pstmt.execute(); + + // Grab handle-reuse before it would be populated if initially created. + if(0 < pstmt.getPreparedStatementHandle()) + testsWithHandleReuse++; + + pstmt.getMoreResults(); // Make sure handle is updated. + } + } + System.out.println(String.format("Prep on first call: %s Query count:%s: %s of %s (%s)", prepOnFirstCall, queryCount, testsWithHandleReuse, testCount, (double)testsWithHandleReuse/(double)testCount)); + } + } + } + + try (SQLServerConnection con = (SQLServerConnection)DriverManager.getConnection(connectionString)) { + + // Test behvaior with statement pooling. + con.setStatementPoolingCacheSize(10); + this.executeSQL(con, + "IF NOT EXISTS (SELECT * FROM sys.messages WHERE message_id = 99586) EXEC sp_addmessage 99586, 16, 'Prepared handle GAH!';"); + // Test with missing handle failures (fake). + this.executeSQL(con, "CREATE TABLE #update1 (col INT);INSERT #update1 VALUES (1);"); + this.executeSQL(con, + "CREATE PROC #updateProc1 AS UPDATE #update1 SET col += 1; IF EXISTS (SELECT * FROM #update1 WHERE col % 5 = 0) RAISERROR(99586,16,1);"); + try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con.prepareStatement("#updateProc1")) { + for (int i = 0; i < 100; ++i) { + try { + assertSame(1, pstmt.executeUpdate()); + } + catch (SQLException e) { + // Error "Prepared handle GAH" is expected to happen. But it should not terminate the execution with RAISERROR. + // Since the original "Could not find prepared statement with handle" error does not terminate the execution after it. + if (!e.getMessage().contains("Prepared handle GAH")) { + throw e; + } + } + } + } + + // test updated value, should be 1 + 100 = 101 + // although executeUpdate() throws exception, update operation should be executed successfully. + try (ResultSet rs = con.createStatement().executeQuery("select * from #update1")) { + rs.next(); + assertSame(101, rs.getInt(1)); + } + + // Test batching with missing handle failures (fake). + this.executeSQL(con, + "IF NOT EXISTS (SELECT * FROM sys.messages WHERE message_id = 99586) EXEC sp_addmessage 99586, 16, 'Prepared handle GAH!';"); + this.executeSQL(con, "CREATE TABLE #update2 (col INT);INSERT #update2 VALUES (1);"); + this.executeSQL(con, + "CREATE PROC #updateProc2 AS UPDATE #update2 SET col += 1; IF EXISTS (SELECT * FROM #update2 WHERE col % 5 = 0) RAISERROR(99586,16,1);"); + try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con.prepareStatement("#updateProc2")) { + for (int i = 0; i < 100; ++i) { + pstmt.addBatch(); + } + + int[] updateCounts = null; + try { + updateCounts = pstmt.executeBatch(); + } + catch (BatchUpdateException e) { + // Error "Prepared handle GAH" is expected to happen. But it should not terminate the execution with RAISERROR. + // Since the original "Could not find prepared statement with handle" error does not terminate the execution after it. + if (!e.getMessage().contains("Prepared handle GAH")) { + throw e; + } + } + + // since executeBatch() throws exception, it does not return anthing. So updateCounts is still null. + assertSame(null, updateCounts); + + // test updated value, should be 1 + 100 = 101 + // although executeBatch() throws exception, update operation should be executed successfully. + try (ResultSet rs = con.createStatement().executeQuery("select * from #update2")) { + rs.next(); + assertSame(101, rs.getInt(1)); + } + } + } + + try (SQLServerConnection con = (SQLServerConnection)DriverManager.getConnection(connectionString)) { + // Test behvaior with statement pooling. + con.setDisableStatementPooling(false); + con.setStatementPoolingCacheSize(10); + + String lookupUniqueifier = UUID.randomUUID().toString(); + String query = String.format("/*statementpoolingtest_%s*/SELECT * FROM sys.tables;", lookupUniqueifier); + + // Execute statement first, should create cache entry WITHOUT handle (since sp_executesql was used). + try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement)con.prepareStatement(query)) { + pstmt.execute(); // sp_executesql + pstmt.getMoreResults(); // Make sure handle is updated. + + assertSame(0, pstmt.getPreparedStatementHandle()); + } + + // Execute statement again, should now create handle. + int handle = 0; + try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement)con.prepareStatement(query)) { + pstmt.execute(); // sp_prepexec + pstmt.getMoreResults(); // Make sure handle is updated. + + handle = pstmt.getPreparedStatementHandle(); + assertNotSame(0, handle); + } + + // Execute statement again and verify same handle was used. + try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement)con.prepareStatement(query)) { + pstmt.execute(); // sp_execute + pstmt.getMoreResults(); // Make sure handle is updated. + + assertNotSame(0, pstmt.getPreparedStatementHandle()); + assertSame(handle, pstmt.getPreparedStatementHandle()); + } + + // Execute new statement with different SQL text and verify it does NOT get same handle (should now fall back to using sp_executesql). + SQLServerPreparedStatement outer = null; + try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement)con.prepareStatement(query + ";")) { + outer = pstmt; + pstmt.execute(); // sp_executesql + pstmt.getMoreResults(); // Make sure handle is updated. + + assertSame(0, pstmt.getPreparedStatementHandle()); + assertNotSame(handle, pstmt.getPreparedStatementHandle()); + } + try { + System.out.println(outer.getPreparedStatementHandle()); + fail("Error for invalid use of getPreparedStatementHandle() after statement close expected."); + } + catch(Exception e) { + // Good! + } + } + } + /** * Test handling of eviction from statement pooling for prepared statements. * From 5cf28ad79a550c0ac84efa9f09cef07ed82684b7 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 1 May 2018 09:34:45 -0700 Subject: [PATCH 16/84] change reflection for testing --- .../sqlserver/jdbc/preparedStatement/RegressionTest.java | 4 +++- .../jdbc/unit/statement/BatchExecuteWithErrorsTest.java | 5 +++-- .../sqlserver/jdbc/unit/statement/PreparedStatementTest.java | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java index 4bbf8cb8d..f866bc1f8 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java @@ -24,6 +24,8 @@ import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; + +import com.microsoft.sqlserver.jdbc.SQLServerConnection; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; @@ -351,7 +353,7 @@ public void batchWithLargeStringTestUseBulkCopyAPI() throws SQLException { String[] values = {"a", "b", largeString, "d", "e"}; // insert five rows into the table; use a batch for each row try { - Field f1 = con.getClass().getSuperclass().getDeclaredField("isAzureDW"); + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); f1.setAccessible(true); f1.set(con, true); pstmt = con.prepareStatement("insert into " + testTable + " values (?,?)"); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java index facdeb0b3..d86e08c39 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java @@ -29,6 +29,7 @@ import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; +import com.microsoft.sqlserver.jdbc.SQLServerConnection; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBConnection; @@ -287,7 +288,7 @@ public void Repro47239UseBulkCopyAPI() throws SQLException, NoSuchFieldException String error; String severe; con = DriverManager.getConnection(connectionString); - Field f1 = con.getClass().getSuperclass().getDeclaredField("isAzureDW"); + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); f1.setAccessible(true); f1.set(con, true); if (DBConnection.isSqlAzure(con)) { @@ -697,7 +698,7 @@ public void Repro47239largeUseBulkCopyAPI() throws Exception { assumeTrue("JDBC42".equals(Utils.getConfiguredProperty("JDBC_Version")), "Aborting test case as JDBC version is not compatible. "); // the DBConnection for detecting whether the server is SQL Azure or SQL Server. con = DriverManager.getConnection(connectionString); - Field f1 = con.getClass().getSuperclass().getDeclaredField("isAzureDW"); + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); f1.setAccessible(true); f1.set(con, true); final String warning; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java index c1f811709..440be6a87 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java @@ -310,7 +310,7 @@ public void testStatementPoolingUseBulkCopyAPI() throws SQLException, NoSuchFiel IllegalArgumentException, IllegalAccessException { // Test % handle re-use try (SQLServerConnection con = (SQLServerConnection)DriverManager.getConnection(connectionString)) { - Field f1 = con.getClass().getSuperclass().getDeclaredField("isAzureDW"); + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); f1.setAccessible(true); f1.set(con, true); String query = String.format("/*statementpoolingtest_re-use_%s*/SELECT TOP(1) * FROM sys.tables;", UUID.randomUUID().toString()); From 59d29d70aff4a590b4b5208d5866ed3c363adee9 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 1 May 2018 09:56:16 -0700 Subject: [PATCH 17/84] more test changes --- .../sqlserver/jdbc/unit/statement/BatchExecutionTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java index 70cb48fc5..7a9bbd437 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java @@ -27,6 +27,7 @@ import org.junit.runner.RunWith; import org.opentest4j.TestAbortedException; +import com.microsoft.sqlserver.jdbc.SQLServerConnection; import com.microsoft.sqlserver.jdbc.SQLServerStatement; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBConnection; @@ -123,7 +124,7 @@ public void testAddBatch1UseBulkCopyAPI() { int retValue[] = {0, 0, 0}; try { String sPrepStmt = "update ctstable2 set PRICE=PRICE*20 where TYPE_ID=?"; - Field f1 = connection.getClass().getSuperclass().getDeclaredField("isAzureDW"); + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); f1.setAccessible(true); f1.set(connection, true); pstmt = connection.prepareStatement(sPrepStmt); @@ -237,7 +238,7 @@ public void testExecuteBatch1UseBulkCopyAPI() { String sPrepStmt = "update ctstable2 set PRICE=PRICE*20 where TYPE_ID=?"; pstmt = connection.prepareStatement(sPrepStmt); - Field f1 = connection.getClass().getSuperclass().getDeclaredField("isAzureDW"); + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); f1.setAccessible(true); f1.set(connection, true); pstmt.setInt(1, 1); From dc42708ef4a3f511e5a6aa2a705ac5ba717a4711 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 1 May 2018 13:08:03 -0700 Subject: [PATCH 18/84] Add parsing logic for -- comment --- .../jdbc/SQLServerPreparedStatement.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 32b8595ac..f8455a51d 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -2634,6 +2634,12 @@ private String parseUserSQLForTableNameDW(boolean hasInsertBeenFound, boolean ha return parseUserSQLForTableNameDW(hasInsertBeenFound, hasIntoBeenFound); } + if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { + int temp = localUserSQL.indexOf("\n") + 2; + localUserSQL = localUserSQL.substring(temp); + return parseUserSQLForTableNameDW(hasInsertBeenFound, hasIntoBeenFound); + } + if (localUserSQL.substring(0, 6).equalsIgnoreCase("insert") && !hasInsertBeenFound) { localUserSQL = localUserSQL.substring(6); return parseUserSQLForTableNameDW(true, hasIntoBeenFound); @@ -2737,6 +2743,12 @@ private ArrayList parseUserSQLForColumnListDW() { return parseUserSQLForColumnListDW(); } + if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { + int temp = localUserSQL.indexOf("\n") + 2; + localUserSQL = localUserSQL.substring(temp); + return parseUserSQLForColumnListDW(); + } + //check if optional column list was provided // Columns can have the form of c1, [c1] or "c1". It can escape ] or " by ]] or "". if (localUserSQL.substring(0, 1).equalsIgnoreCase("(")) { @@ -2756,6 +2768,12 @@ private ArrayList parseUserSQLForColumnListDWHelper(ArrayList li return parseUserSQLForColumnListDWHelper(listOfColumns); } + if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { + int temp = localUserSQL.indexOf("\n") + 2; + localUserSQL = localUserSQL.substring(temp); + return parseUserSQLForColumnListDWHelper(listOfColumns); + } + if (localUserSQL.charAt(0) == ')') { localUserSQL = localUserSQL.substring(1); return listOfColumns; @@ -2836,6 +2854,12 @@ private ArrayList parseUserSQLForValueListDW(boolean hasValuesBeenFound) return parseUserSQLForValueListDW(hasValuesBeenFound); } + if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { + int temp = localUserSQL.indexOf("\n") + 2; + localUserSQL = localUserSQL.substring(temp); + return parseUserSQLForValueListDW(hasValuesBeenFound); + } + if (!hasValuesBeenFound) { // look for keyword "VALUES" if (localUserSQL.substring(0, 6).equalsIgnoreCase("VALUES")) { @@ -2850,6 +2874,12 @@ private ArrayList parseUserSQLForValueListDW(boolean hasValuesBeenFound) return parseUserSQLForValueListDW(true); } + if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { + int temp = localUserSQL.indexOf("\n") + 2; + localUserSQL = localUserSQL.substring(temp); + return parseUserSQLForValueListDW(true); + } + if (localUserSQL.substring(0, 1).equalsIgnoreCase("(")) { localUserSQL = localUserSQL.substring(1); return parseUserSQLForValueListDWHelper(new ArrayList()); @@ -2863,6 +2893,12 @@ private ArrayList parseUserSQLForValueListDW(boolean hasValuesBeenFound) return parseUserSQLForValueListDW(hasValuesBeenFound); } + if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { + int temp = localUserSQL.indexOf("\n") + 2; + localUserSQL = localUserSQL.substring(temp); + return parseUserSQLForValueListDW(hasValuesBeenFound); + } + if (localUserSQL.substring(0, 1).equalsIgnoreCase("(")) { localUserSQL = localUserSQL.substring(1); return parseUserSQLForValueListDWHelper(new ArrayList()); @@ -2883,6 +2919,11 @@ private ArrayList parseUserSQLForValueListDWHelper(ArrayList lis return parseUserSQLForValueListDWHelper(listOfValues); } + if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { + int temp = localUserSQL.indexOf("\n") + 2; + localUserSQL = localUserSQL.substring(temp); + return parseUserSQLForValueListDWHelper(listOfValues); + } if (localUserSQL.charAt(0) == ')') { localUserSQL = localUserSQL.substring(1); @@ -2928,6 +2969,10 @@ private ArrayList parseUserSQLForValueListDWHelper(ArrayList lis int temp = localUserSQL.indexOf("*/") + 2; localUserSQL = localUserSQL.substring(temp); localUserSQL = localUserSQL.trim(); + } else if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { + int temp = localUserSQL.indexOf("\n") + 2; + localUserSQL = localUserSQL.substring(temp); + localUserSQL = localUserSQL.trim(); } else { sb.append(localUserSQL.charAt(0)); localUserSQL = localUserSQL.substring(1); From dca4cb521bdd3455014528c278d2856c5308f1d9 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Thu, 3 May 2018 14:31:51 -0700 Subject: [PATCH 19/84] refactoring --- .../jdbc/SQLServerPreparedStatement.java | 99 +++++-------------- .../sqlserver/jdbc/SQLServerStatement.java | 3 + 2 files changed, 28 insertions(+), 74 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 632771c92..ee410fb9e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -2628,15 +2628,7 @@ private String parseUserSQLForTableNameDW(boolean hasInsertBeenFound, boolean ha // And there could be in-line comments (with /* and */) in between. // This method assumes the localUserSQL string starts with "insert". localUserSQL = localUserSQL.trim(); - if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { - int temp = localUserSQL.indexOf("*/") + 2; - localUserSQL = localUserSQL.substring(temp); - return parseUserSQLForTableNameDW(hasInsertBeenFound, hasIntoBeenFound); - } - - if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { - int temp = localUserSQL.indexOf("\n") + 2; - localUserSQL = localUserSQL.substring(temp); + if (checkAndRemoveComments()) { return parseUserSQLForTableNameDW(hasInsertBeenFound, hasIntoBeenFound); } @@ -2737,15 +2729,7 @@ private ArrayList parseUserSQLForColumnListDW() { localUserSQL = localUserSQL.trim(); // ignore all comments - if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { - int temp = localUserSQL.indexOf("*/") + 2; - localUserSQL = localUserSQL.substring(temp); - return parseUserSQLForColumnListDW(); - } - - if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { - int temp = localUserSQL.indexOf("\n") + 2; - localUserSQL = localUserSQL.substring(temp); + if (checkAndRemoveComments()) { return parseUserSQLForColumnListDW(); } @@ -2762,15 +2746,7 @@ private ArrayList parseUserSQLForColumnListDWHelper(ArrayList li localUserSQL = localUserSQL.trim(); // ignore all comments - if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { - int temp = localUserSQL.indexOf("*/") + 2; - localUserSQL = localUserSQL.substring(temp); - return parseUserSQLForColumnListDWHelper(listOfColumns); - } - - if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { - int temp = localUserSQL.indexOf("\n") + 2; - localUserSQL = localUserSQL.substring(temp); + if (checkAndRemoveComments()) { return parseUserSQLForColumnListDWHelper(listOfColumns); } @@ -2829,9 +2805,7 @@ private ArrayList parseUserSQLForColumnListDWHelper(ArrayList li listOfColumns.add(sb.toString()); return listOfColumns; } - } else if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { - int temp = localUserSQL.indexOf("*/") + 2; - localUserSQL = localUserSQL.substring(temp); + } else if (checkAndRemoveComments()) { localUserSQL = localUserSQL.trim(); } else { sb.append(localUserSQL.charAt(0)); @@ -2848,15 +2822,7 @@ private ArrayList parseUserSQLForValueListDW(boolean hasValuesBeenFound) localUserSQL = localUserSQL.trim(); // ignore all comments - if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { - int temp = localUserSQL.indexOf("*/") + 2; - localUserSQL = localUserSQL.substring(temp); - return parseUserSQLForValueListDW(hasValuesBeenFound); - } - - if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { - int temp = localUserSQL.indexOf("\n") + 2; - localUserSQL = localUserSQL.substring(temp); + if (checkAndRemoveComments()) { return parseUserSQLForValueListDW(hasValuesBeenFound); } @@ -2868,15 +2834,7 @@ private ArrayList parseUserSQLForValueListDW(boolean hasValuesBeenFound) localUserSQL = localUserSQL.trim(); // ignore all comments - if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { - int temp = localUserSQL.indexOf("*/") + 2; - localUserSQL = localUserSQL.substring(temp); - return parseUserSQLForValueListDW(true); - } - - if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { - int temp = localUserSQL.indexOf("\n") + 2; - localUserSQL = localUserSQL.substring(temp); + if (checkAndRemoveComments()) { return parseUserSQLForValueListDW(true); } @@ -2887,15 +2845,7 @@ private ArrayList parseUserSQLForValueListDW(boolean hasValuesBeenFound) } } else { // ignore all comments - if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { - int temp = localUserSQL.indexOf("*/") + 2; - localUserSQL = localUserSQL.substring(temp); - return parseUserSQLForValueListDW(hasValuesBeenFound); - } - - if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { - int temp = localUserSQL.indexOf("\n") + 2; - localUserSQL = localUserSQL.substring(temp); + if (checkAndRemoveComments()) { return parseUserSQLForValueListDW(hasValuesBeenFound); } @@ -2913,18 +2863,10 @@ private ArrayList parseUserSQLForValueListDWHelper(ArrayList lis localUserSQL = localUserSQL.trim(); // ignore all comments - if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { - int temp = localUserSQL.indexOf("*/") + 2; - localUserSQL = localUserSQL.substring(temp); - return parseUserSQLForValueListDWHelper(listOfValues); - } - - if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { - int temp = localUserSQL.indexOf("\n") + 2; - localUserSQL = localUserSQL.substring(temp); + if (checkAndRemoveComments()) { return parseUserSQLForValueListDWHelper(listOfValues); } - + if (localUserSQL.charAt(0) == ')') { localUserSQL = localUserSQL.substring(1); return listOfValues; @@ -2965,13 +2907,7 @@ private ArrayList parseUserSQLForValueListDWHelper(ArrayList lis listOfValues.add(sb.toString()); return listOfValues; } - } else if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { - int temp = localUserSQL.indexOf("*/") + 2; - localUserSQL = localUserSQL.substring(temp); - localUserSQL = localUserSQL.trim(); - } else if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { - int temp = localUserSQL.indexOf("\n") + 2; - localUserSQL = localUserSQL.substring(temp); + } else if (checkAndRemoveComments()) { localUserSQL = localUserSQL.trim(); } else { sb.append(localUserSQL.charAt(0)); @@ -2982,6 +2918,21 @@ private ArrayList parseUserSQLForValueListDWHelper(ArrayList lis // It shouldn't come here. If we did, something is wrong. throw new IllegalArgumentException("localUserSQL"); } + + private boolean checkAndRemoveComments() { + if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { + int temp = localUserSQL.indexOf("*/") + 2; + localUserSQL = localUserSQL.substring(temp); + return true; + } + + if (localUserSQL.substring(0, 2).equalsIgnoreCase("--")) { + int temp = localUserSQL.indexOf("\n") + 2; + localUserSQL = localUserSQL.substring(temp); + return true; + } + return false; + } private final class PrepStmtBatchExecCmd extends TDSCommand { private final SQLServerPreparedStatement stmt; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index 031981dc2..fc531ae49 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -989,6 +989,9 @@ final void resetForReexecute() throws SQLServerException { // Used to check just the first letter which would cause // "Set" commands to return true... String temp = sql.trim(); + if (null == sql || sql.length() < 3) { + return false; + } if (temp.substring(0, 2).equalsIgnoreCase("/*")) { int index = temp.indexOf("*/") + 2; return isInsert(temp.substring(index)); From 8f036b13354b6b7d0aeba46497b5720d71b8e218 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Fri, 4 May 2018 16:27:57 -0700 Subject: [PATCH 20/84] Update snapshot --- build.gradle | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 327812f2d..b4cfce4a7 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ apply plugin: 'java' -version = '6.5.2' +version = '6.5.3-SNAPSHOT' def jreVersion = "" def testOutputDir = file("build/classes/java/test") def archivesBaseName = 'mssql-jdbc' diff --git a/pom.xml b/pom.xml index e6739ad5a..5b39cdd77 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.microsoft.sqlserver mssql-jdbc - 6.5.2.${jreVersion}-preview + 6.5.3-SNAPSHOT.${jreVersion}-preview jar Microsoft JDBC Driver for SQL Server From b67ccfb171758d18ba52144ce74bf307a18f93ea Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 8 May 2018 15:13:57 -0700 Subject: [PATCH 21/84] Bug fix / testing change --- .../sqlserver/jdbc/SQLServerBulkCopy.java | 17 ++++++++++++++--- .../jdbc/SQLServerPreparedStatement.java | 9 ++++++++- .../sqlserver/jdbc/SQLServerStatement.java | 3 +++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java index 27c608d30..846f4c360 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java @@ -153,6 +153,9 @@ private class ColumnMapping { /* The CekTable for the destination table. */ private CekTable destCekTable = null; + /* Statement level encryption setting needed for querying against encrypted columns. */ + private SQLServerStatementColumnEncryptionSetting stmtColumnEncriptionSetting = SQLServerStatementColumnEncryptionSetting.UseConnectionSetting; + /* * Metadata for the destination table columns */ @@ -1740,11 +1743,13 @@ private void getDestinationMetadata() throws SQLServerException { SQLServerResultSet rs = null; SQLServerResultSet rsMoreMetaData = null; - + SQLServerStatement stmt = null; + try { + stmt = (SQLServerStatement) connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, connection.getHoldability(), stmtColumnEncriptionSetting); // Get destination metadata - rs = ((SQLServerStatement) connection.createStatement()) - .executeQueryInternal("SET FMTONLY ON SELECT * FROM " + destinationTableName + " SET FMTONLY OFF "); + rs = stmt.executeQueryInternal("SET FMTONLY ON SELECT * FROM " + destinationTableName + " SET FMTONLY OFF "); destColumnCount = rs.getMetaData().getColumnCount(); destColumnMetadata = new HashMap<>(); @@ -1783,6 +1788,8 @@ private void getDestinationMetadata() throws SQLServerException { finally { if (null != rs) rs.close(); + if (null != stmt) + stmt.close(); if (null != rsMoreMetaData) rsMoreMetaData.close(); } @@ -3573,4 +3580,8 @@ private boolean writeBatchData(TDSWriter tdsWriter, } } } + + protected void setStmtColumnEncriptionSetting(SQLServerStatementColumnEncryptionSetting stmtColumnEncriptionSetting) { + this.stmtColumnEncriptionSetting = stmtColumnEncriptionSetting; + } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index ee410fb9e..bd840705e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -2472,8 +2472,10 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL ArrayList valueList = parseUserSQLForValueListDW(false); String destinationTableName = tableName; + SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, connection.getHoldability(), stmtColumnEncriptionSetting); // Get destination metadata - try (SQLServerResultSet rs = ((SQLServerStatement) connection.createStatement()) + try (SQLServerResultSet rs = stmt .executeQueryInternal("SET FMTONLY ON SELECT * FROM " + destinationTableName + " SET FMTONLY OFF ");) { SQLServerBulkBatchInsertRecord batchRecord = new SQLServerBulkBatchInsertRecord(batchParamValues, columnList, valueList, null); @@ -2493,6 +2495,7 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL SQLServerBulkCopy bcOperation = new SQLServerBulkCopy(connection); bcOperation.setDestinationTableName(tableName); + bcOperation.setStmtColumnEncriptionSetting(this.getStmtColumnEncriptionSetting()); bcOperation.writeToServer((ISQLServerBulkRecord) batchRecord); bcOperation.close(); updateCounts = new int[batchParamValues.size()]; @@ -2505,6 +2508,10 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL loggerExternal.exiting(getClassNameLogging(), "executeBatch", updateCounts); return updateCounts; } + finally { + if (null != stmt) + stmt.close(); + } } } catch (SQLException e) { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index fc531ae49..1b89a5e58 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -154,6 +154,9 @@ String getClassNameLogging() { */ protected SQLServerStatementColumnEncryptionSetting stmtColumnEncriptionSetting = SQLServerStatementColumnEncryptionSetting.UseConnectionSetting; + protected SQLServerStatementColumnEncryptionSetting getStmtColumnEncriptionSetting() { + return stmtColumnEncriptionSetting; + } /** * ExecuteProperties encapsulates a subset of statement property values as they were set at execution time. */ From a677b284ce65ff139f34bc2d144ef10664ab64c0 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 8 May 2018 16:05:36 -0700 Subject: [PATCH 22/84] Reflect comment change --- .../com/microsoft/sqlserver/jdbc/SQLServerConnection.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 60fc59dde..db54b2398 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -5888,13 +5888,7 @@ boolean isAzureDW() throws SQLServerException, SQLException { // Base data type: int final int ENGINE_EDITION_FOR_SQL_AZURE_DW = 6; rs.next(); - int engineEdition = rs.getInt(1); - if (engineEdition == ENGINE_EDITION_FOR_SQL_AZURE_DW) - { - isAzureDW = true; - } else { - isAzureDW = false; - } + isAzureDW = (rs.getInt(1) == ENGINE_EDITION_FOR_SQL_AZURE_DW) ? true : false; } return isAzureDW; } else { From e05abee5d9661af1d371671ab4c0b76d80f39d65 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Fri, 11 May 2018 17:13:51 -0700 Subject: [PATCH 23/84] Feature | AKV Old Constructor changes - Reformatted code + Deprecated old Constructor and added a new constructor with 1 param --- .../sqlserver/jdbc/KeyVaultCredential.java | 27 +++++----- ...ColumnEncryptionAzureKeyVaultProvider.java | 49 +++++++++++++------ 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java b/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java index c13354da7..6eff2830e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java @@ -24,30 +24,33 @@ */ class KeyVaultCredential extends KeyVaultCredentials { - SQLServerKeyVaultAuthenticationCallback authenticationCallback = null; - String clientId = null; - String clientKey = null; - String accessToken = null; + SQLServerKeyVaultAuthenticationCallback authenticationCallback = null; + String clientId = null; + String clientKey = null; + String accessToken = null; KeyVaultCredential(String clientId, String clientKey) { this.clientId = clientId; this.clientKey = clientKey; } - + KeyVaultCredential(SQLServerKeyVaultAuthenticationCallback authenticationCallback) { this.authenticationCallback = authenticationCallback; } - + public String doAuthenticate(String authorization, String resource, String scope) { - if(authenticationCallback==null) { - AuthenticationResult token = getAccessTokenFromClientCredentials(authorization, resource, clientId, clientKey); - return token.getAccessToken(); - }else { - return authenticationCallback.getAccessToken(authorization, resource, scope); - } + String accessToken; + if (authenticationCallback == null) { + AuthenticationResult token = getAccessTokenFromClientCredentials(authorization, resource, clientId, clientKey); + accessToken = token.getAccessToken(); + } + else { + accessToken = authenticationCallback.getAccessToken(authorization, resource, scope); + } + return accessToken; } private static AuthenticationResult getAccessTokenFromClientCredentials(String authorization, diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java index b307738b7..8402709c0 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java @@ -37,7 +37,7 @@ * Provides implementation similar to certificate store provider. A CEK encrypted with certificate store provider should be decryptable by this * provider and vice versa. * - * Envolope Format for the encrypted column encryption key version + keyPathLength + ciphertextLength + keyPath + ciphertext + signature version: A + * Envelope Format for the encrypted column encryption key version + keyPathLength + ciphertextLength + keyPath + ciphertext + signature version: A * single byte indicating the format version. keyPathLength: Length of the keyPath. ciphertextLength: ciphertext length keyPath: keyPath used to * encrypt the column encryption key. This is only used for troubleshooting purposes and is not verified during decryption. ciphertext: Encrypted * column encryption key signature: Signature of the entire byte array. Signature is validated before decrypting the column encryption key. @@ -48,9 +48,9 @@ public class SQLServerColumnEncryptionAzureKeyVaultProvider extends SQLServerCol * Column Encryption Key Store Provider string */ String name = "AZURE_KEY_VAULT"; - + private final String baseUrl = "https://{vaultBaseUrl}"; - + private final String azureKeyVaultDomainName = "vault.azure.net"; private final String rsaEncryptionAlgorithmWithOAEPForAKV = "RSA-OAEP"; @@ -71,18 +71,21 @@ public void setName(String name) { public String getName() { return this.name; } - + /** * Constructor that takes a callback function to authenticate to AAD. This is used by KeyVaultClient at runtime to authenticate to Azure Key * Vault. * + * This constructor is present to maintain backwards compatibility with 6.0 version of the driver. Deprecated for removal in next Stable release. + * * @param authenticationCallback * - Callback function used for authenticating to AAD. * @param executorService - * - The ExecutorService used to create the keyVaultClient + * - The ExecutorService, previously used to create the keyVaultClient, but not in use anymore. - This param can be passed as 'null' * @throws SQLServerException * when an error occurs */ + @Deprecated public SQLServerColumnEncryptionAzureKeyVaultProvider(SQLServerKeyVaultAuthenticationCallback authenticationCallback, ExecutorService executorService) throws SQLServerException { if (null == authenticationCallback) { @@ -90,20 +93,38 @@ public SQLServerColumnEncryptionAzureKeyVaultProvider(SQLServerKeyVaultAuthentic Object[] msgArgs1 = {"SQLServerKeyVaultAuthenticationCallback"}; throw new SQLServerException(form.format(msgArgs1), null); } - credentials = new KeyVaultCredential(authenticationCallback); - RestClient restClient = new RestClient.Builder(new OkHttpClient.Builder(), new Retrofit.Builder()) - .withBaseUrl(baseUrl) - .withCredentials(credentials) - .withSerializerAdapter(new AzureJacksonAdapter()) - .withResponseBuilderFactory(new AzureResponseBuilder.Factory()) - .build(); + credentials = new KeyVaultCredential(authenticationCallback); + RestClient restClient = new RestClient.Builder(new OkHttpClient.Builder(), new Retrofit.Builder()).withBaseUrl(baseUrl) + .withCredentials(credentials).withSerializerAdapter(new AzureJacksonAdapter()) + .withResponseBuilderFactory(new AzureResponseBuilder.Factory()).build(); keyVaultClient = new KeyVaultClient(restClient); } - + /** - * Constructor that authenticates to AAD. This is used by KeyVaultClient at runtime to authenticate to Azure Key + * Constructor that takes a callback function to authenticate to AAD. This is used by KeyVaultClient at runtime to authenticate to Azure Key * Vault. * + * @param authenticationCallback + * - Callback function used for authenticating to AAD. + * @throws SQLServerException + * when an error occurs + */ + public SQLServerColumnEncryptionAzureKeyVaultProvider(SQLServerKeyVaultAuthenticationCallback authenticationCallback) throws SQLServerException { + if (null == authenticationCallback) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); + Object[] msgArgs1 = {"SQLServerKeyVaultAuthenticationCallback"}; + throw new SQLServerException(form.format(msgArgs1), null); + } + credentials = new KeyVaultCredential(authenticationCallback); + RestClient restClient = new RestClient.Builder(new OkHttpClient.Builder(), new Retrofit.Builder()).withBaseUrl(baseUrl) + .withCredentials(credentials).withSerializerAdapter(new AzureJacksonAdapter()) + .withResponseBuilderFactory(new AzureResponseBuilder.Factory()).build(); + keyVaultClient = new KeyVaultClient(restClient); + } + + /** + * Constructor that authenticates to AAD. This is used by KeyVaultClient at runtime to authenticate to Azure Key Vault. + * * @param clientId * Identifier of the client requesting the token. * @param clientKey From 8cf4afb707985d6ee98e88dbabeaebca6726eca2 Mon Sep 17 00:00:00 2001 From: ulvii Date: Tue, 15 May 2018 09:55:58 -0700 Subject: [PATCH 24/84] Mark computed columns as IS_GENERATEDCOLUMN in the result set returned by getColumns() (#695) * Fix | getColumns() API, changed column name from SS_IS_COMPUTED to IS_AUTOINCREMENT per JDBC specs | issue #600 * Fix | getColumns() API, changed column name from SS_IS_COMPUTED to IS_GENERATEDCOLUMN per JDBC specs | issue #600 --- .../microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index fb9cd7add..4d3ab8f45 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -225,7 +225,7 @@ private void checkClosed() throws SQLServerException { private static final String FUNCTION_TYPE = "FUNCTION_TYPE"; private static final String SS_IS_SPARSE = "SS_IS_SPARSE"; private static final String SS_IS_COLUMN_SET = "SS_IS_COLUMN_SET"; - private static final String SS_IS_COMPUTED = "SS_IS_COMPUTED"; + private static final String IS_GENERATEDCOLUMN = "IS_GENERATEDCOLUMN"; private static final String IS_AUTOINCREMENT = "IS_AUTOINCREMENT"; /** @@ -565,7 +565,7 @@ private static String EscapeIDName(String inID) throws SQLServerException { private static final String[] getColumnsColumnNamesKatmai = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ COLUMN_NAME, /* 5 */ DATA_TYPE, /* 6 */ TYPE_NAME, /* 7 */ COLUMN_SIZE, /* 8 */ BUFFER_LENGTH, /* 9 */ DECIMAL_DIGITS, /* 10 */ NUM_PREC_RADIX, /* 11 */ NULLABLE, /* 12 */ REMARKS, /* 13 */ COLUMN_DEF, /* 14 */ SQL_DATA_TYPE, /* 15 */ SQL_DATETIME_SUB, /* 16 */ CHAR_OCTET_LENGTH, - /* 17 */ ORDINAL_POSITION, /* 18 */ IS_NULLABLE, /* 20 */ SS_IS_SPARSE, /* 20 */ SS_IS_COLUMN_SET, /* 21 */ SS_IS_COMPUTED, + /* 17 */ ORDINAL_POSITION, /* 18 */ IS_NULLABLE, /* 20 */ SS_IS_SPARSE, /* 20 */ SS_IS_COLUMN_SET, /* 21 */ IS_GENERATEDCOLUMN, /* 22 */ IS_AUTOINCREMENT}; /* L0 */ public java.sql.ResultSet getColumns(String catalog, From 4b128db96ed96d76b9624eb3ea63d47558c74d07 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 15 May 2018 15:46:54 -0700 Subject: [PATCH 25/84] fix issue with redirection --- .../microsoft/sqlserver/jdbc/IOBuffer.java | 7 ++--- .../sqlserver/jdbc/SQLServerConnection.java | 26 ++++++++++++++----- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index fa0ed78c5..5d9e1652e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -1589,11 +1589,8 @@ void enableSSL(String host, String trustStoreFileName = con.activeConnectionProperties.getProperty(SQLServerDriverStringProperty.TRUST_STORE.toString()); String trustStorePassword = con.activeConnectionProperties.getProperty(SQLServerDriverStringProperty.TRUST_STORE_PASSWORD.toString()); - String hostNameInCertificate = con.getNewlyRoutedHostName(); - if (null == hostNameInCertificate || hostNameInCertificate.isEmpty()) { - hostNameInCertificate = con.activeConnectionProperties - .getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString()); - } + String hostNameInCertificate = con.activeConnectionProperties + .getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString()); trustStoreType = con.activeConnectionProperties.getProperty(SQLServerDriverStringProperty.TRUST_STORE_TYPE.toString()); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index e7745bb51..9b6fab643 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -116,6 +116,8 @@ public class SQLServerConnection implements ISQLServerConnection { private byte[] accessTokenInByte = null; private SqlFedAuthToken fedAuthToken = null; + + private String originalHostNameInCertificate = null; static class Sha1HashKey { private byte[] bytes; @@ -397,16 +399,11 @@ private enum State { // Contains the routing info received from routing ENVCHANGE private ServerPortPlaceHolder routingInfo = null; - private String newlyRoutedHostName = null; ServerPortPlaceHolder getRoutingInfo() { return routingInfo; } - String getNewlyRoutedHostName() { - return newlyRoutedHostName; - } - // Permission targets private static final String callAbortPerm = "callAbort"; @@ -1203,6 +1200,23 @@ Connection connectInternal(Properties propsIn, activeConnectionProperties = (Properties) propsIn.clone(); pooledConnectionParent = pooledConnection; + + String hostNameInCertificate = activeConnectionProperties. + getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString()); + + // hostNameInCertificate property can change when redirection is involved, so maintain this value + // for every instance of SQLServerConnection. + if (null == originalHostNameInCertificate && null != hostNameInCertificate && !hostNameInCertificate.isEmpty()) { + originalHostNameInCertificate = activeConnectionProperties. + getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString()); + } + + if (null != originalHostNameInCertificate && !originalHostNameInCertificate.isEmpty()) { + // if hostNameInCertificate has a legitimate value (and not empty or null), + // reset hostNameInCertificate to the original value every time we connect (or re-connect). + activeConnectionProperties.setProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString(), + originalHostNameInCertificate); + } String sPropKey; String sPropValue; @@ -3675,7 +3689,7 @@ final void processEnvChange(TDSReader tdsReader) throws SQLServerException { if (hostNameNeedsUpdate) { String newHostName = "*" + routingServerName.substring(routingServerName.indexOf('.')); - newlyRoutedHostName = newHostName; + activeConnectionProperties.setProperty("hostNameInCertificate", newHostName); if (connectionlogger.isLoggable(Level.FINER)) { connectionlogger.finer(toString() + "Using new host to validate the SSL certificate"); From d27677b550280aed7671e1d5a2d195a659db377a Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Tue, 22 May 2018 16:16:23 -0700 Subject: [PATCH 26/84] Fix | PS Caching - Remove commented lines --- .../microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 4784b40e5..891528753 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -267,11 +267,7 @@ private CallableStatement getCallableStatementHandle(CallableHandles request, hassoc = new HandleAssociation(catalog, CS); HandleAssociation previous = handleMap.put(request, hassoc); if (null != previous) { - //((SQLServerCallableStatement) previous.stmt).handleDBName = previous.databaseName; - previous.close(); - - //((SQLServerCallableStatement) previous.stmt).handleDBName = null; } } return hassoc.stmt; From c48cef50a830100ed63760871be15c7714586241 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Wed, 23 May 2018 15:38:03 -0700 Subject: [PATCH 27/84] Trigger Appveyor test --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 5fff6778c..cc473aded 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -29,7 +29,7 @@ cache: - C:\Users\appveyor\.m2 -> pom.xml build_script: - - keytool -importkeystore -srckeystore cert.pfx -srcstoretype pkcs12 -destkeystore clientcert.jks -deststoretype JKS -srcstorepass password -deststorepass password + - keytool -importkeystore -srckeystore cert.pfx -srcstoretype pkcs12 -destkeystore clientcert.jks -deststoretype JKS -srcstorepass password -deststorepass password - keytool -list -v -keystore clientcert.jks -storepass "password" > JavaKeyStore.txt - cd.. - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pbuild43 From fc1303c6576c111ed71cc8edbc6ccd65f49b5703 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Wed, 23 May 2018 15:54:03 -0700 Subject: [PATCH 28/84] Fix | AKV Old Constructor - Calling the other constructor instead. --- ...QLServerColumnEncryptionAzureKeyVaultProvider.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java index 8402709c0..632f8135f 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java @@ -88,16 +88,7 @@ public String getName() { @Deprecated public SQLServerColumnEncryptionAzureKeyVaultProvider(SQLServerKeyVaultAuthenticationCallback authenticationCallback, ExecutorService executorService) throws SQLServerException { - if (null == authenticationCallback) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); - Object[] msgArgs1 = {"SQLServerKeyVaultAuthenticationCallback"}; - throw new SQLServerException(form.format(msgArgs1), null); - } - credentials = new KeyVaultCredential(authenticationCallback); - RestClient restClient = new RestClient.Builder(new OkHttpClient.Builder(), new Retrofit.Builder()).withBaseUrl(baseUrl) - .withCredentials(credentials).withSerializerAdapter(new AzureJacksonAdapter()) - .withResponseBuilderFactory(new AzureResponseBuilder.Factory()).build(); - keyVaultClient = new KeyVaultClient(restClient); + this(authenticationCallback); } /** From 252cacdac4f4fa07c7d2c574f9fd77eeb0f70464 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Wed, 23 May 2018 16:20:11 -0700 Subject: [PATCH 29/84] Fix | Reversed null checks --- .../java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java b/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java index 6eff2830e..beacad4c6 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java @@ -43,7 +43,7 @@ public String doAuthenticate(String authorization, String resource, String scope) { String accessToken; - if (authenticationCallback == null) { + if (null == authenticationCallback) { AuthenticationResult token = getAccessTokenFromClientCredentials(authorization, resource, clientId, clientKey); accessToken = token.getAccessToken(); } @@ -74,7 +74,7 @@ private static AuthenticationResult getAccessTokenFromClientCredentials(String a service.shutdown(); } - if (result == null) { + if (null == result) { throw new RuntimeException("authentication result was null"); } return result; From e3bdaec00dd8add23f2c555c055877b8b578fcde Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Thu, 24 May 2018 10:57:51 -0700 Subject: [PATCH 30/84] Resolving alignment problems and comments --- .../com/microsoft/sqlserver/jdbc/KeyVaultCredential.java | 6 +++--- .../SQLServerColumnEncryptionAzureKeyVaultProvider.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java b/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java index beacad4c6..470078242 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java @@ -25,9 +25,9 @@ class KeyVaultCredential extends KeyVaultCredentials { SQLServerKeyVaultAuthenticationCallback authenticationCallback = null; - String clientId = null; - String clientKey = null; - String accessToken = null; + String clientId = null; + String clientKey = null; + String accessToken = null; KeyVaultCredential(String clientId, String clientKey) { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java index 632f8135f..201f13c7b 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java @@ -76,12 +76,12 @@ public String getName() { * Constructor that takes a callback function to authenticate to AAD. This is used by KeyVaultClient at runtime to authenticate to Azure Key * Vault. * - * This constructor is present to maintain backwards compatibility with 6.0 version of the driver. Deprecated for removal in next Stable release. + * This constructor is present to maintain backwards compatibility with 6.0 version of the driver. Deprecated for removal in next stable release. * * @param authenticationCallback * - Callback function used for authenticating to AAD. * @param executorService - * - The ExecutorService, previously used to create the keyVaultClient, but not in use anymore. - This param can be passed as 'null' + * - The ExecutorService, previously used to create the keyVaultClient, but not in use anymore. - This parameter can be passed as 'null' * @throws SQLServerException * when an error occurs */ @@ -121,7 +121,7 @@ public SQLServerColumnEncryptionAzureKeyVaultProvider(SQLServerKeyVaultAuthentic * @param clientKey * Key of the client requesting the token. * @throws SQLServerException - * when an error occurs + * when an error occurs */ public SQLServerColumnEncryptionAzureKeyVaultProvider(String clientId, String clientKey) throws SQLServerException { From 60b437e67e9c1db15baa4e057dcb4ee079cfc907 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Fri, 25 May 2018 10:22:27 -0700 Subject: [PATCH 31/84] Refactor two Bulk files into a common parent --- .../jdbc/SQLServerBulkBatchInsertRecord.java | 288 +---------------- .../jdbc/SQLServerBulkCSVFileRecord.java | 281 +---------------- .../sqlserver/jdbc/SQLServerBulkCommon.java | 297 ++++++++++++++++++ .../sqlserver/jdbc/SQLServerConnection.java | 17 +- .../sqlserver/jdbc/SQLServerDataSource.java | 20 ++ .../sqlserver/jdbc/SQLServerDriver.java | 6 +- .../jdbc/SQLServerPreparedStatement.java | 21 +- .../sqlserver/jdbc/SQLServerResource.java | 1 + 8 files changed, 364 insertions(+), 567 deletions(-) create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java index 3b570cd57..2b4b39179 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java @@ -18,7 +18,6 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.HashMap; -import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -26,60 +25,7 @@ * A simple implementation of the ISQLServerBulkRecord interface that can be used to read in the basic Java data types from an ArrayList of * Parameters that were provided by pstmt/cstmt. */ -public class SQLServerBulkBatchInsertRecord implements ISQLServerBulkRecord, java.lang.AutoCloseable { - /* - * Class to represent the column metadata - */ - private class ColumnMetadata { - String columnName; - int columnType; - int precision; - int scale; - DateTimeFormatter dateTimeFormatter = null; - - ColumnMetadata(String name, - int type, - int precision, - int scale, - DateTimeFormatter dateTimeFormatter) { - columnName = name; - columnType = type; - this.precision = precision; - this.scale = scale; - this.dateTimeFormatter = dateTimeFormatter; - } - } - - /* - * Metadata to represent the columns in the batch. Each column should be mapped to its corresponding position within the parameter (from position 1 and - * onwards) - */ - private Map columnMetadata; - - /* - * Contains all the column names if firstLineIsColumnNames is true - */ - private String[] columnNames = null; - - /* - * Contains the format that java.sql.Types.TIMESTAMP_WITH_TIMEZONE data should be read in as. - */ - private DateTimeFormatter dateTimeFormatter = null; - - /* - * Contains the format that java.sql.Types.TIME_WITH_TIMEZONE data should be read in as. - */ - private DateTimeFormatter timeFormatter = null; - - /* - * Class name for logging. - */ - private static final String loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkBatchInsertRecord"; - - /* - * Logger - */ - private static final java.util.logging.Logger loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); +public class SQLServerBulkBatchInsertRecord extends SQLServerBulkCommon implements ISQLServerBulkRecord, java.lang.AutoCloseable { private ArrayList batchParam; private int batchParamIndex = -1; @@ -88,6 +34,7 @@ private class ColumnMetadata { public SQLServerBulkBatchInsertRecord(ArrayList batchParam, ArrayList columnList, ArrayList valueList, String encoding) throws SQLServerException { + loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkBatchInsertRecord"; loggerExternal.entering(loggerClassName, "SQLServerBulkBatchInsertRecord", new Object[] {batchParam, encoding}); @@ -108,200 +55,7 @@ public SQLServerBulkBatchInsertRecord(ArrayList batchParam, ArrayLi } /** - * Adds metadata for the given column in the file. - * - * @param positionInTable - * Indicates which column the metadata is for. Columns start at 1. - * @param name - * Name for the column (optional if only using column ordinal in a mapping for SQLServerBulkCopy operation) - * @param jdbcType - * JDBC data type of the column - * @param precision - * Precision for the column (ignored for the appropriate data types) - * @param scale - * Scale for the column (ignored for the appropriate data types) - * @param dateTimeFormatter - * format to parse data that is sent - * @throws SQLServerException - * when an error occurs - */ - public void addColumnMetadata(int positionInTable, - String name, - int jdbcType, - int precision, - int scale, - DateTimeFormatter dateTimeFormatter) throws SQLServerException { - addColumnMetadataInternal(positionInTable, name, jdbcType, precision, scale, dateTimeFormatter); - } - - /** - * Adds metadata for the given column in the file. - * - * @param positionInTable - * Indicates which column the metadata is for. Columns start at 1. - * @param name - * Name for the column (optional if only using column ordinal in a mapping for SQLServerBulkCopy operation) - * @param jdbcType - * JDBC data type of the column - * @param precision - * Precision for the column (ignored for the appropriate data types) - * @param scale - * Scale for the column (ignored for the appropriate data types) - * @throws SQLServerException - * when an error occurs - */ - public void addColumnMetadata(int positionInTable, - String name, - int jdbcType, - int precision, - int scale) throws SQLServerException { - addColumnMetadataInternal(positionInTable, name, jdbcType, precision, scale, null); - } - - /** - * Adds metadata for the given column in the file. - * - * @param positionInTable - * Indicates which column the metadata is for. Columns start at 1. - * @param name - * Name for the column (optional if only using column ordinal in a mapping for SQLServerBulkCopy operation) - * @param jdbcType - * JDBC data type of the column - * @param precision - * Precision for the column (ignored for the appropriate data types) - * @param scale - * Scale for the column (ignored for the appropriate data types) - * @param dateTimeFormatter - * format to parse data that is sent - * @throws SQLServerException - * when an error occurs - */ - void addColumnMetadataInternal(int positionInTable, - String name, - int jdbcType, - int precision, - int scale, - DateTimeFormatter dateTimeFormatter) throws SQLServerException { - loggerExternal.entering(loggerClassName, "addColumnMetadata", new Object[] {positionInTable, name, jdbcType, precision, scale}); - - String colName = ""; - - if (0 >= positionInTable) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumnOrdinal")); - Object[] msgArgs = {positionInTable}; - throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); - } - - if (null != name) - colName = name.trim(); - else if ((columnNames != null) && (columnNames.length >= positionInTable)) - colName = columnNames[positionInTable - 1]; - - if ((columnNames != null) && (positionInTable > columnNames.length)) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumn")); - Object[] msgArgs = {positionInTable}; - throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); - } - - checkDuplicateColumnName(positionInTable, name); - switch (jdbcType) { - /* - * SQL Server supports numerous string literal formats for temporal types, hence sending them as varchar with approximate - * precision(length) needed to send supported string literals. string literal formats supported by temporal types are available in MSDN - * page on data types. - */ - case java.sql.Types.DATE: - case java.sql.Types.TIME: - case java.sql.Types.TIMESTAMP: - case microsoft.sql.Types.DATETIMEOFFSET: - // The precision is just a number long enough to hold all types of temporal data, doesn't need to be exact precision. - columnMetadata.put(positionInTable, new ColumnMetadata(colName, jdbcType, 50, scale, dateTimeFormatter)); - break; - - // Redirect SQLXML as LONGNVARCHAR - // SQLXML is not valid type in TDS - case java.sql.Types.SQLXML: - columnMetadata.put(positionInTable, new ColumnMetadata(colName, java.sql.Types.LONGNVARCHAR, precision, scale, dateTimeFormatter)); - break; - - // Redirecting Float as Double based on data type mapping - // https://msdn.microsoft.com/en-us/library/ms378878%28v=sql.110%29.aspx - case java.sql.Types.FLOAT: - columnMetadata.put(positionInTable, new ColumnMetadata(colName, java.sql.Types.DOUBLE, precision, scale, dateTimeFormatter)); - break; - - // redirecting BOOLEAN as BIT - case java.sql.Types.BOOLEAN: - columnMetadata.put(positionInTable, new ColumnMetadata(colName, java.sql.Types.BIT, precision, scale, dateTimeFormatter)); - break; - - default: - columnMetadata.put(positionInTable, new ColumnMetadata(colName, jdbcType, precision, scale, dateTimeFormatter)); - } - - loggerExternal.exiting(loggerClassName, "addColumnMetadata"); - } - - /** - * Set the format for reading in dates from the file. - * - * @param dateTimeFormat - * format to parse data sent as java.sql.Types.TIMESTAMP_WITH_TIMEZONE - */ - public void setTimestampWithTimezoneFormat(String dateTimeFormat) { - DriverJDBCVersion.checkSupportsJDBC42(); - loggerExternal.entering(loggerClassName, "setTimestampWithTimezoneFormat", dateTimeFormat); - - this.dateTimeFormatter = DateTimeFormatter.ofPattern(dateTimeFormat); - - loggerExternal.exiting(loggerClassName, "setTimestampWithTimezoneFormat"); - } - - /** - * Set the format for reading in dates from the file. - * - * @param dateTimeFormatter - * format to parse data sent as java.sql.Types.TIMESTAMP_WITH_TIMEZONE - */ - public void setTimestampWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { - loggerExternal.entering(loggerClassName, "setTimestampWithTimezoneFormat", new Object[] {dateTimeFormatter}); - - this.dateTimeFormatter = dateTimeFormatter; - - loggerExternal.exiting(loggerClassName, "setTimestampWithTimezoneFormat"); - } - - /** - * Set the format for reading in dates from the file. - * - * @param timeFormat - * format to parse data sent as java.sql.Types.TIME_WITH_TIMEZONE - */ - public void setTimeWithTimezoneFormat(String timeFormat) { - DriverJDBCVersion.checkSupportsJDBC42(); - loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", timeFormat); - - this.timeFormatter = DateTimeFormatter.ofPattern(timeFormat); - - loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); - } - - /** - * Set the format for reading in dates from the file. - * - * @param dateTimeFormatter - * format to parse data sent as java.sql.Types.TIME_WITH_TIMEZONE - */ - public void setTimeWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { - loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", new Object[] {dateTimeFormatter}); - - this.timeFormatter = dateTimeFormatter; - - loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); - } - - /** - * Releases any resources associated with the file reader. + * Releases any resources associated with the batch. * * @throws SQLServerException * when an error occurs @@ -479,13 +233,6 @@ public Object[] getRowData() throws SQLServerException { case Types.VARBINARY: case Types.LONGVARBINARY: case Types.BLOB: { - /* - * For binary data, the value in file may or may not have the '0x' prefix. We will try to match our implementation with - * 'BULK INSERT' except that we will allow 0x prefix whereas 'BULK INSERT' command does not allow 0x prefix. A BULK INSERT - * example: A sample csv file containing data for 2 binary columns and 1 row: 61,62 Table definition: create table t1(c1 - * varbinary(10), c2 varbinary(10)) BULK INSERT command: bulk insert t1 from 'C:\in.csv' - * with(DATAFILETYPE='char',firstrow=1,FIELDTERMINATOR=',') select * from t1 shows 1 row with columns: 0x61, 0x62 - */ // Strip off 0x if present. String binData = data[pair.getKey() - 1].toString().trim(); if (binData.startsWith("0x") || binData.startsWith("0X")) { @@ -566,33 +313,4 @@ public boolean next() throws SQLServerException { batchParamIndex++; return batchParamIndex < batchParam.size(); } - - /* - * Helper method to throw a SQLServerExeption with the invalidArgument message and given argument. - */ - private void throwInvalidArgument(String argument) throws SQLServerException { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument")); - Object[] msgArgs = {argument}; - SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, false); - } - - /* - * Method to throw a SQLServerExeption for duplicate column names - */ - private void checkDuplicateColumnName(int positionInTable, - String colName) throws SQLServerException { - - if (null != colName && colName.trim().length() != 0) { - for (Entry entry : columnMetadata.entrySet()) { - // duplicate check is not performed in case of same positionInTable value - if (null != entry && entry.getKey() != positionInTable) { - if (null != entry.getValue() && colName.trim().equalsIgnoreCase(entry.getValue().columnName)) { - throw new SQLServerException(SQLServerException.getErrString("R_BulkCSVDataDuplicateColumn"), null); - } - } - - } - } - - } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java index cce49a416..3fc1bcc87 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java @@ -23,7 +23,6 @@ import java.time.OffsetTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; -import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -31,30 +30,7 @@ * A simple implementation of the ISQLServerBulkRecord interface that can be used to read in the basic Java data types from a delimited file where * each line represents a row of data. */ -public class SQLServerBulkCSVFileRecord implements ISQLServerBulkRecord, java.lang.AutoCloseable { - /* - * Class to represent the column metadata - */ - private class ColumnMetadata { - String columnName; - int columnType; - int precision; - int scale; - DateTimeFormatter dateTimeFormatter = null; - - ColumnMetadata(String name, - int type, - int precision, - int scale, - DateTimeFormatter dateTimeFormatter) { - columnName = name; - columnType = type; - this.precision = precision; - this.scale = scale; - this.dateTimeFormatter = dateTimeFormatter; - } - } - +public class SQLServerBulkCSVFileRecord extends SQLServerBulkCommon implements ISQLServerBulkRecord, java.lang.AutoCloseable { /* * Resources associated with reading in the file */ @@ -62,12 +38,6 @@ private class ColumnMetadata { private InputStreamReader sr; private FileInputStream fis; - /* - * Metadata to represent the columns in the file. Each column should be mapped to its corresponding position within the file (from position 1 and - * onwards) - */ - private Map columnMetadata; - /* * Current line of data to parse. */ @@ -78,31 +48,6 @@ private class ColumnMetadata { */ private final String delimiter; - /* - * Contains all the column names if firstLineIsColumnNames is true - */ - private String[] columnNames = null; - - /* - * Contains the format that java.sql.Types.TIMESTAMP_WITH_TIMEZONE data should be read in as. - */ - private DateTimeFormatter dateTimeFormatter = null; - - /* - * Contains the format that java.sql.Types.TIME_WITH_TIMEZONE data should be read in as. - */ - private DateTimeFormatter timeFormatter = null; - - /* - * Class name for logging. - */ - private static final String loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkCSVFileRecord"; - - /* - * Logger - */ - private static final java.util.logging.Logger loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); - /** * Creates a simple reader to parse data from a delimited file with the given encoding. * @@ -121,6 +66,7 @@ public SQLServerBulkCSVFileRecord(String fileToParse, String encoding, String delimiter, boolean firstLineIsColumnNames) throws SQLServerException { + loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkCSVFileRecord"; loggerExternal.entering(loggerClassName, "SQLServerBulkCSVFileRecord", new Object[] {fileToParse, encoding, delimiter, firstLineIsColumnNames}); @@ -181,6 +127,7 @@ public SQLServerBulkCSVFileRecord(InputStream fileToParse, String encoding, String delimiter, boolean firstLineIsColumnNames) throws SQLServerException { + loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkCSVFileRecord"; loggerExternal.entering(loggerClassName, "SQLServerBulkCSVFileRecord", new Object[] {fileToParse, encoding, delimiter, firstLineIsColumnNames}); @@ -253,199 +200,6 @@ public SQLServerBulkCSVFileRecord(String fileToParse, this(fileToParse, null, ",", firstLineIsColumnNames); } - /** - * Adds metadata for the given column in the file. - * - * @param positionInFile - * Indicates which column the metadata is for. Columns start at 1. - * @param name - * Name for the column (optional if only using column ordinal in a mapping for SQLServerBulkCopy operation) - * @param jdbcType - * JDBC data type of the column - * @param precision - * Precision for the column (ignored for the appropriate data types) - * @param scale - * Scale for the column (ignored for the appropriate data types) - * @param dateTimeFormatter - * format to parse data that is sent - * @throws SQLServerException - * when an error occurs - */ - public void addColumnMetadata(int positionInFile, - String name, - int jdbcType, - int precision, - int scale, - DateTimeFormatter dateTimeFormatter) throws SQLServerException { - addColumnMetadataInternal(positionInFile, name, jdbcType, precision, scale, dateTimeFormatter); - } - - /** - * Adds metadata for the given column in the file. - * - * @param positionInFile - * Indicates which column the metadata is for. Columns start at 1. - * @param name - * Name for the column (optional if only using column ordinal in a mapping for SQLServerBulkCopy operation) - * @param jdbcType - * JDBC data type of the column - * @param precision - * Precision for the column (ignored for the appropriate data types) - * @param scale - * Scale for the column (ignored for the appropriate data types) - * @throws SQLServerException - * when an error occurs - */ - public void addColumnMetadata(int positionInFile, - String name, - int jdbcType, - int precision, - int scale) throws SQLServerException { - addColumnMetadataInternal(positionInFile, name, jdbcType, precision, scale, null); - } - - /** - * Adds metadata for the given column in the file. - * - * @param positionInFile - * Indicates which column the metadata is for. Columns start at 1. - * @param name - * Name for the column (optional if only using column ordinal in a mapping for SQLServerBulkCopy operation) - * @param jdbcType - * JDBC data type of the column - * @param precision - * Precision for the column (ignored for the appropriate data types) - * @param scale - * Scale for the column (ignored for the appropriate data types) - * @param dateTimeFormatter - * format to parse data that is sent - * @throws SQLServerException - * when an error occurs - */ - void addColumnMetadataInternal(int positionInFile, - String name, - int jdbcType, - int precision, - int scale, - DateTimeFormatter dateTimeFormatter) throws SQLServerException { - loggerExternal.entering(loggerClassName, "addColumnMetadata", new Object[] {positionInFile, name, jdbcType, precision, scale}); - - String colName = ""; - - if (0 >= positionInFile) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumnOrdinal")); - Object[] msgArgs = {positionInFile}; - throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); - } - - if (null != name) - colName = name.trim(); - else if ((columnNames != null) && (columnNames.length >= positionInFile)) - colName = columnNames[positionInFile - 1]; - - if ((columnNames != null) && (positionInFile > columnNames.length)) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumn")); - Object[] msgArgs = {positionInFile}; - throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); - } - - checkDuplicateColumnName(positionInFile, name); - switch (jdbcType) { - /* - * SQL Server supports numerous string literal formats for temporal types, hence sending them as varchar with approximate - * precision(length) needed to send supported string literals. string literal formats supported by temporal types are available in MSDN - * page on data types. - */ - case java.sql.Types.DATE: - case java.sql.Types.TIME: - case java.sql.Types.TIMESTAMP: - case microsoft.sql.Types.DATETIMEOFFSET: - // The precision is just a number long enough to hold all types of temporal data, doesn't need to be exact precision. - columnMetadata.put(positionInFile, new ColumnMetadata(colName, jdbcType, 50, scale, dateTimeFormatter)); - break; - - // Redirect SQLXML as LONGNVARCHAR - // SQLXML is not valid type in TDS - case java.sql.Types.SQLXML: - columnMetadata.put(positionInFile, new ColumnMetadata(colName, java.sql.Types.LONGNVARCHAR, precision, scale, dateTimeFormatter)); - break; - - // Redirecting Float as Double based on data type mapping - // https://msdn.microsoft.com/en-us/library/ms378878%28v=sql.110%29.aspx - case java.sql.Types.FLOAT: - columnMetadata.put(positionInFile, new ColumnMetadata(colName, java.sql.Types.DOUBLE, precision, scale, dateTimeFormatter)); - break; - - // redirecting BOOLEAN as BIT - case java.sql.Types.BOOLEAN: - columnMetadata.put(positionInFile, new ColumnMetadata(colName, java.sql.Types.BIT, precision, scale, dateTimeFormatter)); - break; - - default: - columnMetadata.put(positionInFile, new ColumnMetadata(colName, jdbcType, precision, scale, dateTimeFormatter)); - } - - loggerExternal.exiting(loggerClassName, "addColumnMetadata"); - } - - /** - * Set the format for reading in dates from the file. - * - * @param dateTimeFormat - * format to parse data sent as java.sql.Types.TIMESTAMP_WITH_TIMEZONE - */ - public void setTimestampWithTimezoneFormat(String dateTimeFormat) { - DriverJDBCVersion.checkSupportsJDBC42(); - loggerExternal.entering(loggerClassName, "setTimestampWithTimezoneFormat", dateTimeFormat); - - this.dateTimeFormatter = DateTimeFormatter.ofPattern(dateTimeFormat); - - loggerExternal.exiting(loggerClassName, "setTimestampWithTimezoneFormat"); - } - - /** - * Set the format for reading in dates from the file. - * - * @param dateTimeFormatter - * format to parse data sent as java.sql.Types.TIMESTAMP_WITH_TIMEZONE - */ - public void setTimestampWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { - loggerExternal.entering(loggerClassName, "setTimestampWithTimezoneFormat", new Object[] {dateTimeFormatter}); - - this.dateTimeFormatter = dateTimeFormatter; - - loggerExternal.exiting(loggerClassName, "setTimestampWithTimezoneFormat"); - } - - /** - * Set the format for reading in dates from the file. - * - * @param timeFormat - * format to parse data sent as java.sql.Types.TIME_WITH_TIMEZONE - */ - public void setTimeWithTimezoneFormat(String timeFormat) { - DriverJDBCVersion.checkSupportsJDBC42(); - loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", timeFormat); - - this.timeFormatter = DateTimeFormatter.ofPattern(timeFormat); - - loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); - } - - /** - * Set the format for reading in dates from the file. - * - * @param dateTimeFormatter - * format to parse data sent as java.sql.Types.TIME_WITH_TIMEZONE - */ - public void setTimeWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { - loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", new Object[] {dateTimeFormatter}); - - this.timeFormatter = dateTimeFormatter; - - loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); - } - /** * Releases any resources associated with the file reader. * @@ -720,33 +474,4 @@ public boolean next() throws SQLServerException { } return (null != currentLine); } - - /* - * Helper method to throw a SQLServerExeption with the invalidArgument message and given argument. - */ - private void throwInvalidArgument(String argument) throws SQLServerException { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument")); - Object[] msgArgs = {argument}; - SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, false); - } - - /* - * Method to throw a SQLServerExeption for duplicate column names - */ - private void checkDuplicateColumnName(int positionInFile, - String colName) throws SQLServerException { - - if (null != colName && colName.trim().length() != 0) { - for (Entry entry : columnMetadata.entrySet()) { - // duplicate check is not performed in case of same positionInFile value - if (null != entry && entry.getKey() != positionInFile) { - if (null != entry.getValue() && colName.trim().equalsIgnoreCase(entry.getValue().columnName)) { - throw new SQLServerException(SQLServerException.getErrString("R_BulkCSVDataDuplicateColumn"), null); - } - } - - } - } - - } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java new file mode 100644 index 000000000..b5b832d54 --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java @@ -0,0 +1,297 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + +package com.microsoft.sqlserver.jdbc; + +import java.text.MessageFormat; +import java.time.format.DateTimeFormatter; +import java.util.Map; +import java.util.Map.Entry; + +abstract class SQLServerBulkCommon { + + /* + * Class to represent the column metadata + */ + protected class ColumnMetadata { + String columnName; + int columnType; + int precision; + int scale; + DateTimeFormatter dateTimeFormatter = null; + + ColumnMetadata(String name, + int type, + int precision, + int scale, + DateTimeFormatter dateTimeFormatter) { + columnName = name; + columnType = type; + this.precision = precision; + this.scale = scale; + this.dateTimeFormatter = dateTimeFormatter; + } + } + + /* + * Class name for logging. + */ + protected static String loggerClassName; + + /* + * Logger + */ + protected static final java.util.logging.Logger loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); + + /* + * Contains all the column names if firstLineIsColumnNames is true + */ + protected String[] columnNames = null; + + /* + * Metadata to represent the columns in the batch/file. Each column should be mapped to its corresponding position within the parameter (from position 1 and + * onwards) + */ + protected Map columnMetadata; + + /* + * Contains the format that java.sql.Types.TIMESTAMP_WITH_TIMEZONE data should be read in as. + */ + protected DateTimeFormatter dateTimeFormatter = null; + + /* + * Contains the format that java.sql.Types.TIME_WITH_TIMEZONE data should be read in as. + */ + protected DateTimeFormatter timeFormatter = null; + + /** + * Adds metadata for the given column in the batch/file. + * + * @param positionInSource + * Indicates which column the metadata is for. Columns start at 1. + * @param name + * Name for the column (optional if only using column ordinal in a mapping for SQLServerBulkCopy operation) + * @param jdbcType + * JDBC data type of the column + * @param precision + * Precision for the column (ignored for the appropriate data types) + * @param scale + * Scale for the column (ignored for the appropriate data types) + * @param dateTimeFormatter + * format to parse data that is sent + * @throws SQLServerException + * when an error occurs + */ + public void addColumnMetadata(int positionInSource, + String name, + int jdbcType, + int precision, + int scale, + DateTimeFormatter dateTimeFormatter) throws SQLServerException { + addColumnMetadataInternal(positionInSource, name, jdbcType, precision, scale, dateTimeFormatter); + } + + /** + * Adds metadata for the given column in the batch/file. + * + * @param positionInTable + * Indicates which column the metadata is for. Columns start at 1. + * @param name + * Name for the column (optional if only using column ordinal in a mapping for SQLServerBulkCopy operation) + * @param jdbcType + * JDBC data type of the column + * @param precision + * Precision for the column (ignored for the appropriate data types) + * @param scale + * Scale for the column (ignored for the appropriate data types) + * @throws SQLServerException + * when an error occurs + */ + public void addColumnMetadata(int positionInSource, + String name, + int jdbcType, + int precision, + int scale) throws SQLServerException { + addColumnMetadataInternal(positionInSource, name, jdbcType, precision, scale, null); + } + + /** + * Adds metadata for the given column in the batch/file. + * + * @param positionInSource + * Indicates which column the metadata is for. Columns start at 1. + * @param name + * Name for the column (optional if only using column ordinal in a mapping for SQLServerBulkCopy operation) + * @param jdbcType + * JDBC data type of the column + * @param precision + * Precision for the column (ignored for the appropriate data types) + * @param scale + * Scale for the column (ignored for the appropriate data types) + * @param dateTimeFormatter + * format to parse data that is sent + * @throws SQLServerException + * when an error occurs + */ + void addColumnMetadataInternal(int positionInSource, + String name, + int jdbcType, + int precision, + int scale, + DateTimeFormatter dateTimeFormatter) throws SQLServerException { + loggerExternal.entering(loggerClassName, "addColumnMetadata", new Object[] {positionInSource, name, jdbcType, precision, scale}); + + String colName = ""; + + if (0 >= positionInSource) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumnOrdinal")); + Object[] msgArgs = {positionInSource}; + throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); + } + + if (null != name) + colName = name.trim(); + else if ((columnNames != null) && (columnNames.length >= positionInSource)) + colName = columnNames[positionInSource - 1]; + + if ((columnNames != null) && (positionInSource > columnNames.length)) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumn")); + Object[] msgArgs = {positionInSource}; + throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); + } + + checkDuplicateColumnName(positionInSource, name); + switch (jdbcType) { + /* + * SQL Server supports numerous string literal formats for temporal types, hence sending them as varchar with approximate + * precision(length) needed to send supported string literals. string literal formats supported by temporal types are available in MSDN + * page on data types. + */ + case java.sql.Types.DATE: + case java.sql.Types.TIME: + case java.sql.Types.TIMESTAMP: + case microsoft.sql.Types.DATETIMEOFFSET: + if (this instanceof SQLServerBulkCSVFileRecord) { + columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, 50, precision, dateTimeFormatter)); + } else if (this instanceof SQLServerBulkBatchInsertRecord) { + columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, precision, precision, dateTimeFormatter)); + } else { + columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, 50, precision, dateTimeFormatter)); + } + break; + + // Redirect SQLXML as LONGNVARCHAR + // SQLXML is not valid type in TDS + case java.sql.Types.SQLXML: + columnMetadata.put(positionInSource, new ColumnMetadata(colName, java.sql.Types.LONGNVARCHAR, precision, scale, dateTimeFormatter)); + break; + + // Redirecting Float as Double based on data type mapping + // https://msdn.microsoft.com/en-us/library/ms378878%28v=sql.110%29.aspx + case java.sql.Types.FLOAT: + columnMetadata.put(positionInSource, new ColumnMetadata(colName, java.sql.Types.DOUBLE, precision, scale, dateTimeFormatter)); + break; + + // redirecting BOOLEAN as BIT + case java.sql.Types.BOOLEAN: + columnMetadata.put(positionInSource, new ColumnMetadata(colName, java.sql.Types.BIT, precision, scale, dateTimeFormatter)); + break; + + default: + columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, precision, scale, dateTimeFormatter)); + } + + loggerExternal.exiting(loggerClassName, "addColumnMetadata"); + } + + /** + * Set the format for reading in dates from the batch/file. + * + * @param dateTimeFormat + * format to parse data sent as java.sql.Types.TIMESTAMP_WITH_TIMEZONE + */ + public void setTimestampWithTimezoneFormat(String dateTimeFormat) { + DriverJDBCVersion.checkSupportsJDBC42(); + loggerExternal.entering(loggerClassName, "setTimestampWithTimezoneFormat", dateTimeFormat); + + this.dateTimeFormatter = DateTimeFormatter.ofPattern(dateTimeFormat); + + loggerExternal.exiting(loggerClassName, "setTimestampWithTimezoneFormat"); + } + + /** + * Set the format for reading in dates from the batch/file. + * + * @param dateTimeFormatter + * format to parse data sent as java.sql.Types.TIMESTAMP_WITH_TIMEZONE + */ + public void setTimestampWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { + loggerExternal.entering(loggerClassName, "setTimestampWithTimezoneFormat", new Object[] {dateTimeFormatter}); + + this.dateTimeFormatter = dateTimeFormatter; + + loggerExternal.exiting(loggerClassName, "setTimestampWithTimezoneFormat"); + } + + /** + * Set the format for reading in dates from the batch/file. + * + * @param timeFormat + * format to parse data sent as java.sql.Types.TIME_WITH_TIMEZONE + */ + public void setTimeWithTimezoneFormat(String timeFormat) { + DriverJDBCVersion.checkSupportsJDBC42(); + loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", timeFormat); + + this.timeFormatter = DateTimeFormatter.ofPattern(timeFormat); + + loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); + } + + /** + * Set the format for reading in dates from the batch/file. + * + * @param dateTimeFormatter + * format to parse data sent as java.sql.Types.TIME_WITH_TIMEZONE + */ + public void setTimeWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { + loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", new Object[] {dateTimeFormatter}); + + this.timeFormatter = dateTimeFormatter; + + loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); + } + + /* + * Helper method to throw a SQLServerExeption with the invalidArgument message and given argument. + */ + protected void throwInvalidArgument(String argument) throws SQLServerException { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument")); + Object[] msgArgs = {argument}; + SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, false); + } + + /* + * Method to throw a SQLServerExeption for duplicate column names + */ + protected void checkDuplicateColumnName(int positionInTable, + String colName) throws SQLServerException { + + if (null != colName && colName.trim().length() != 0) { + for (Entry entry : columnMetadata.entrySet()) { + // duplicate check is not performed in case of same positionInTable value + if (null != entry && entry.getKey() != positionInTable) { + if (null != entry.getValue() && colName.trim().equalsIgnoreCase(entry.getValue().columnName)) { + throw new SQLServerException(SQLServerException.getErrString("R_BulkCSVDataDuplicateColumn"), null); + } + } + + } + } + } +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index db54b2398..01d4a9b70 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -492,6 +492,15 @@ final int getSocketTimeoutMilliseconds() { return socketTimeoutMilliseconds; } + /** + * boolean value for deciding if the driver should use bulk copy API for batch inserts + */ + private boolean useBulkCopyForBatchInsertOnDW; + + final boolean getUseBulkCopyForBatchInsertOnDW() { + return useBulkCopyForBatchInsertOnDW; + } + boolean userSetTNIR = true; private boolean sendTimeAsDatetime = SQLServerDriverBooleanProperty.SEND_TIME_AS_DATETIME.getDefaultValue(); @@ -1746,7 +1755,13 @@ else if (0 == requestedPacketSize) if (null != sPropValue) { setEnablePrepareOnFirstPreparedStatementCall(booleanPropertyOn(sPropKey, sPropValue)); } - + + sPropKey = SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT_ON_DW.toString(); + sPropValue = activeConnectionProperties.getProperty(sPropKey); + if (null != sPropValue) { + useBulkCopyForBatchInsertOnDW = booleanPropertyOn(sPropKey, sPropValue); + } + sPropKey = SQLServerDriverStringProperty.SSL_PROTOCOL.toString(); sPropValue = activeConnectionProperties.getProperty(sPropKey); if (null == sPropValue) { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java index 353c0ddd1..5720b5d24 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java @@ -821,6 +821,26 @@ public int getSocketTimeout() { int defaultTimeOut = SQLServerDriverIntProperty.SOCKET_TIMEOUT.getDefaultValue(); return getIntProperty(connectionProps, SQLServerDriverIntProperty.SOCKET_TIMEOUT.toString(), defaultTimeOut); } + + /** + * Setting the use Bulk Copy API for Batch Insert on Azure Data Warehouse boolean + * + * @param useBulkCopyForBatchInsertOnDW indicates whether Bulk Copy API should be used for Batch Insert operations. + */ + public void setUseBulkCopyForBatchInsertOnDW(boolean useBulkCopyForBatchInsertOnDW) { + setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT_ON_DW.toString(), + useBulkCopyForBatchInsertOnDW); + } + + /** + * Getting the use Bulk Copy API for Batch Insert on Azure Data Warehouse boolean + * + * @return whether the driver should use Bulk Copy API for Batch Insert operations. + */ + public boolean getUseBulkCopyForBatchInsertOnDW() { + return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT_ON_DW.toString(), + SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT_ON_DW.getDefaultValue()); + } /** * Sets the login configuration file for Kerberos authentication. This diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java index 9f13dc3f4..0ea70f7e4 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java @@ -353,7 +353,8 @@ enum SQLServerDriverBooleanProperty TRUST_SERVER_CERTIFICATE ("trustServerCertificate", false), XOPEN_STATES ("xopenStates", false), FIPS ("fips", false), - ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT("enablePrepareOnFirstPreparedStatementCall", SQLServerConnection.DEFAULT_ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT_CALL); + ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT("enablePrepareOnFirstPreparedStatementCall", SQLServerConnection.DEFAULT_ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT_CALL), + USE_BULK_COPY_FOR_BATCH_INSERT_ON_DW ("useBulkCopyForBatchInsertOnDW", false); private final String name; private final boolean defaultValue; @@ -429,7 +430,8 @@ public final class SQLServerDriver implements java.sql.Driver { new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.STATEMENT_POOLING_CACHE_SIZE.toString(), Integer.toString(SQLServerDriverIntProperty.STATEMENT_POOLING_CACHE_SIZE.getDefaultValue()), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.JAAS_CONFIG_NAME.toString(), SQLServerDriverStringProperty.JAAS_CONFIG_NAME.getDefaultValue(), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.SSL_PROTOCOL.toString(), SQLServerDriverStringProperty.SSL_PROTOCOL.getDefaultValue(), false, new String[] {SSLProtocol.TLS.toString(), SSLProtocol.TLS_V10.toString(), SSLProtocol.TLS_V11.toString(), SSLProtocol.TLS_V12.toString()}), - new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.toString(), Integer.toString(SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.getDefaultValue()), false, null), + new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.toString(), Integer.toString(SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.getDefaultValue()), false, null), + new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT_ON_DW.toString(), Boolean.toString(SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT_ON_DW.getDefaultValue()),false, TRUE_FALSE), }; // Properties that can only be set by using Properties. diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index bd840705e..2f33b7662 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -107,6 +107,19 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS private void setPreparedStatementHandle(int handle) { this.prepStmtHandle = handle; } + + /** + * boolean value for deciding if the driver should use bulk copy API for batch inserts + */ + private boolean useBulkCopyForBatchInsertOnDW; + + public boolean getUseBulkCopyForBatchInsertOnDW() { + return useBulkCopyForBatchInsertOnDW; + } + + public void setUseBulkCopyForBatchInsertOnDW(boolean useBulkCopyForBatchInsertOnDW) { + this.useBulkCopyForBatchInsertOnDW = useBulkCopyForBatchInsertOnDW; + } /** The server handle for this prepared statement. If a value {@literal <} 1 is returned no handle has been created. * @@ -2460,7 +2473,7 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL localUserSQL = userSQL; try { - if (isInsert(localUserSQL) && connection.isAzureDW()) { + if (isInsert(localUserSQL) && true && (this.useBulkCopyForBatchInsertOnDW || connection.getUseBulkCopyForBatchInsertOnDW())) { if (batchParamValues == null) { updateCounts = new int[0]; loggerExternal.exiting(getClassNameLogging(), "executeBatch", updateCounts); @@ -2817,6 +2830,7 @@ private ArrayList parseUserSQLForColumnListDWHelper(ArrayList li } else { sb.append(localUserSQL.charAt(0)); localUserSQL = localUserSQL.substring(1); + localUserSQL = localUserSQL.trim(); } } @@ -2919,6 +2933,7 @@ private ArrayList parseUserSQLForValueListDWHelper(ArrayList lis } else { sb.append(localUserSQL.charAt(0)); localUserSQL = localUserSQL.substring(1); + localUserSQL = localUserSQL.trim(); } } @@ -2927,6 +2942,10 @@ private ArrayList parseUserSQLForValueListDWHelper(ArrayList lis } private boolean checkAndRemoveComments() { + if (null == localUserSQL || localUserSQL.length() < 2) { + return false; + } + if (localUserSQL.substring(0, 2).equalsIgnoreCase("/*")) { int temp = localUserSQL.indexOf("*/") + 2; localUserSQL = localUserSQL.substring(temp); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 884be5c93..a2d726519 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -393,5 +393,6 @@ protected Object[][] getContents() { {"R_invalidSSLProtocol", "SSL Protocol {0} label is not valid. Only TLS, TLSv1, TLSv1.1, and TLSv1.2 are supported."}, {"R_cancelQueryTimeoutPropertyDescription", "The number of seconds to wait to cancel sending a query timeout."}, {"R_invalidCancelQueryTimeout", "The cancel timeout value {0} is not valid."}, + {"R_useBulkCopyForBatchInsertOnDWPropertyDescription", "Whether the driver will use bulk copy API for batch insert operations on Azure Data Warehouse."}, }; } \ No newline at end of file From 8604ff46f10e51fd8be745705a413f8ad4535c58 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Fri, 25 May 2018 11:20:46 -0700 Subject: [PATCH 32/84] javadoc changes --- .../sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java | 1 + .../microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java | 2 ++ .../com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java index 2b4b39179..62d990d21 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java @@ -35,6 +35,7 @@ public class SQLServerBulkBatchInsertRecord extends SQLServerBulkCommon implemen public SQLServerBulkBatchInsertRecord(ArrayList batchParam, ArrayList columnList, ArrayList valueList, String encoding) throws SQLServerException { loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkBatchInsertRecord"; + loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); loggerExternal.entering(loggerClassName, "SQLServerBulkBatchInsertRecord", new Object[] {batchParam, encoding}); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java index 3fc1bcc87..9840e2c24 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java @@ -67,6 +67,7 @@ public SQLServerBulkCSVFileRecord(String fileToParse, String delimiter, boolean firstLineIsColumnNames) throws SQLServerException { loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkCSVFileRecord"; + loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); loggerExternal.entering(loggerClassName, "SQLServerBulkCSVFileRecord", new Object[] {fileToParse, encoding, delimiter, firstLineIsColumnNames}); @@ -128,6 +129,7 @@ public SQLServerBulkCSVFileRecord(InputStream fileToParse, String delimiter, boolean firstLineIsColumnNames) throws SQLServerException { loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkCSVFileRecord"; + loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); loggerExternal.entering(loggerClassName, "SQLServerBulkCSVFileRecord", new Object[] {fileToParse, encoding, delimiter, firstLineIsColumnNames}); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java index b5b832d54..4aeb93d98 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java @@ -46,7 +46,7 @@ protected class ColumnMetadata { /* * Logger */ - protected static final java.util.logging.Logger loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); + protected java.util.logging.Logger loggerExternal; /* * Contains all the column names if firstLineIsColumnNames is true @@ -99,7 +99,7 @@ public void addColumnMetadata(int positionInSource, /** * Adds metadata for the given column in the batch/file. * - * @param positionInTable + * @param positionInSource * Indicates which column the metadata is for. Columns start at 1. * @param name * Name for the column (optional if only using column ordinal in a mapping for SQLServerBulkCopy operation) From 9cb981a27c2f4ffeb07a880bac5019245bf52d11 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Fri, 25 May 2018 16:55:29 -0700 Subject: [PATCH 33/84] Applied formatter --- .../sqlserver/jdbc/SQLServerConnection.java | 6 +- .../jdbc/SQLServerPreparedStatement.java | 75 ++++++++++--------- 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index aff73a2fd..d07ca576a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -119,14 +119,14 @@ public class SQLServerConnection implements ISQLServerConnection { static class Sha1HashKey { private byte[] bytes; - + Sha1HashKey(String sql, String parametersDefinition) { this(String.format("%s%s", sql, parametersDefinition)); } - + Sha1HashKey(String s) { - bytes = getSha1Digest().digest(s.getBytes()); + bytes = getSha1Digest().digest(s.getBytes()); } public boolean equals(Object obj) { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 10b1fb912..ac8e66fe3 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -127,15 +127,16 @@ private boolean hasPreparedStatementHandle() { return 0 < prepStmtHandle; } - /** Resets the server handle for this prepared statement to no handle. - */ + /** + * Resets the server handle for this prepared statement to no handle. + */ private boolean resetPrepStmtHandle(boolean discardCurrentCacheItem) { - boolean statementPoolingUsed = null != cachedPreparedStatementHandle; + boolean statementPoolingUsed = null != cachedPreparedStatementHandle; // Return to pool and decrement reference count if (statementPoolingUsed) { // Make sure the cached handle does not get re-used more. - if(discardCurrentCacheItem) - cachedPreparedStatementHandle.setIsExplicitlyDiscarded(); + if (discardCurrentCacheItem) + cachedPreparedStatementHandle.setIsExplicitlyDiscarded(); } prepStmtHandle = 0; return statementPoolingUsed; @@ -578,13 +579,16 @@ else if (EXECUTE_UPDATE == executeMethod && null != resultSet) { /** Should the execution be retried because the re-used cached handle could not be re-used due to server side state changes? */ private boolean retryBasedOnFailedReuseOfCachedHandle(SQLException e, - int attempt, boolean needsPrepare, boolean isBatch) { + int attempt, + boolean needsPrepare, + boolean isBatch) { // Only retry based on these error codes and if statementPooling is enabled: // 586: The prepared statement handle %d is not valid in this context. Please verify that current database, user default schema, and // ANSI_NULLS and QUOTED_IDENTIFIER set options are not changed since the handle is prepared. // 8179: Could not find prepared statement with handle %d. - if(needsPrepare && !isBatch) return false; - return 1 == attempt && (586 == e.getErrorCode() || 8179 == e.getErrorCode()) && connection.isStatementPoolingEnabled(); + if (needsPrepare && !isBatch) + return false; + return 1 == attempt && (586 == e.getErrorCode() || 8179 == e.getErrorCode()) && connection.isStatementPoolingEnabled(); } /** @@ -934,29 +938,31 @@ private void getParameterEncryptionMetadata(Parameter[] params) throws SQLServer connection.resetCurrentCommand(); } - /** Manage re-using cached handles */ - private boolean reuseCachedHandle(boolean hasNewTypeDefinitions, boolean discardCurrentCacheItem) { - // No re-use of caching for cursorable statements (statements that WILL use sp_cursor*) - if (isCursorable(executeMethod)) - return false; - - // If current cache items needs to be discarded or New type definitions found with existing cached handle reference then deregister cached handle. - if (discardCurrentCacheItem || hasNewTypeDefinitions) { - if (null != cachedPreparedStatementHandle && (discardCurrentCacheItem || (hasPreparedStatementHandle() && prepStmtHandle == cachedPreparedStatementHandle.getHandle()))) { - cachedPreparedStatementHandle.removeReference(); - } - + /** Manage re-using cached handles */ + private boolean reuseCachedHandle(boolean hasNewTypeDefinitions, + boolean discardCurrentCacheItem) { + // No re-use of caching for cursorable statements (statements that WILL use sp_cursor*) + if (isCursorable(executeMethod)) + return false; + + // If current cache items needs to be discarded or New type definitions found with existing cached handle reference then deregister cached + // handle. + if (discardCurrentCacheItem || hasNewTypeDefinitions) { + if (null != cachedPreparedStatementHandle + && (discardCurrentCacheItem || (hasPreparedStatementHandle() && prepStmtHandle == cachedPreparedStatementHandle.getHandle()))) { + cachedPreparedStatementHandle.removeReference(); + } + // Make sure the cached handle does not get re-used more if it should be discarded - resetPrepStmtHandle(discardCurrentCacheItem); - cachedPreparedStatementHandle = null; - if(discardCurrentCacheItem) - return false; - } - + resetPrepStmtHandle(discardCurrentCacheItem); + cachedPreparedStatementHandle = null; + if (discardCurrentCacheItem) + return false; + } + // Check for new cache reference. if (null == cachedPreparedStatementHandle) { - PreparedStatementHandle cachedHandle = connection.getCachedPreparedStatementHandle( - new Sha1HashKey(preparedSQL, preparedTypeDefinitions)); + PreparedStatementHandle cachedHandle = connection.getCachedPreparedStatementHandle(new Sha1HashKey(preparedSQL, preparedTypeDefinitions)); // If handle was found then re-use, only if AE is not on and is not a batch query with new type definitions (We shouldn't reuse handle // if it is batch query and has new type definition, or if it is on, make sure encryptionMetadataIsRetrieved is retrieved. if (null != cachedHandle) { @@ -977,13 +983,13 @@ private boolean doPrepExec(TDSWriter tdsWriter, Parameter[] params, boolean hasNewTypeDefinitions, boolean hasExistingTypeDefinitions) throws SQLServerException { - + boolean needsPrepare = (hasNewTypeDefinitions && hasExistingTypeDefinitions) || !hasPreparedStatementHandle(); // Cursors don't use statement pooling. if (isCursorable(executeMethod)) { - - if (needsPrepare) + + if (needsPrepare) buildServerCursorPrepExecParams(tdsWriter); else buildServerCursorExecParams(tdsWriter); @@ -991,15 +997,12 @@ private boolean doPrepExec(TDSWriter tdsWriter, else { // Move overhead of needing to do prepare & unprepare to only use cases that need more than one execution. // First execution, use sp_executesql, optimizing for asumption we will not re-use statement. - if (needsPrepare - && !connection.getEnablePrepareOnFirstPreparedStatementCall() - && !isExecutedAtLeastOnce - ) { + if (needsPrepare && !connection.getEnablePrepareOnFirstPreparedStatementCall() && !isExecutedAtLeastOnce) { buildExecSQLParams(tdsWriter); isExecutedAtLeastOnce = true; } // Second execution, use prepared statements since we seem to be re-using it. - else if(needsPrepare) + else if (needsPrepare) buildPrepExecParams(tdsWriter); else buildExecParams(tdsWriter); From 34d8bb13902904957a7a74a04001cc1c604ff00c Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Mon, 28 May 2018 09:57:09 -0700 Subject: [PATCH 34/84] fix problem with precision / scale --- .../com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java index 4aeb93d98..fc4c79679 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java @@ -177,11 +177,11 @@ else if ((columnNames != null) && (columnNames.length >= positionInSource)) case java.sql.Types.TIMESTAMP: case microsoft.sql.Types.DATETIMEOFFSET: if (this instanceof SQLServerBulkCSVFileRecord) { - columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, 50, precision, dateTimeFormatter)); + columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, 50, scale, dateTimeFormatter)); } else if (this instanceof SQLServerBulkBatchInsertRecord) { - columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, precision, precision, dateTimeFormatter)); + columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, precision, scale, dateTimeFormatter)); } else { - columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, 50, precision, dateTimeFormatter)); + columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, 50, scale, dateTimeFormatter)); } break; From cbea8bde2ecf49a3be75c3d4f11e4bee76d2d596 Mon Sep 17 00:00:00 2001 From: ulvii Date: Mon, 28 May 2018 16:58:09 -0700 Subject: [PATCH 35/84] Fix | Fix some of the Javadoc warnings (#702) --- .../com/microsoft/sqlserver/jdbc/SQLServerConnection.java | 8 ++++---- .../com/microsoft/sqlserver/jdbc/SQLServerDataSource.java | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 377f45859..6e64d9885 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -5722,18 +5722,18 @@ final void unprepareUnreferencedPreparedStatementHandles(boolean force) { } /** - * Returns true if statement pooling is disabled. + * Determine whether statement pooling is disabled. * - * @return + * @return true if statement pooling is disabled, false if it is enabled. */ public boolean getDisableStatementPooling() { return this.disableStatementPooling; } /** - * Sets statement pooling to true or false; + * Disable/enable statement pooling. * - * @param value + * @param value true to disable statement pooling, false to enable it. */ public void setDisableStatementPooling(boolean value) { this.disableStatementPooling = value; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java index 353c0ddd1..96d25f106 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java @@ -784,16 +784,16 @@ public int getStatementPoolingCacheSize() { } /** - * Sets the statement pooling to true or false - * @param disableStatementPooling + * Disable/enable statement pooling. + * @param disableStatementPooling true to disable statement pooling, false to enable it. */ public void setDisableStatementPooling(boolean disableStatementPooling) { setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.DISABLE_STATEMENT_POOLING.toString(), disableStatementPooling); } /** - * Returns true if statement pooling is disabled. - * @return + * Determine whether statement pooling is disabled. + * @return true if statement pooling is disabled, false if it is enabled. */ public boolean getDisableStatementPooling() { boolean defaultValue = SQLServerDriverBooleanProperty.DISABLE_STATEMENT_POOLING.getDefaultValue(); From 39060b7e7f07f2ba0e880baa9395ba4449717673 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 29 May 2018 10:19:00 -0700 Subject: [PATCH 36/84] fix issue with setting all to true --- .../microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 2f33b7662..d2e195379 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -2473,7 +2473,7 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL localUserSQL = userSQL; try { - if (isInsert(localUserSQL) && true && (this.useBulkCopyForBatchInsertOnDW || connection.getUseBulkCopyForBatchInsertOnDW())) { + if (isInsert(localUserSQL) && connection.isAzureDW() && (this.useBulkCopyForBatchInsertOnDW || connection.getUseBulkCopyForBatchInsertOnDW())) { if (batchParamValues == null) { updateCounts = new int[0]; loggerExternal.exiting(getClassNameLogging(), "executeBatch", updateCounts); From 115336296d70eb49b6e792aabe621ab8b93dee51 Mon Sep 17 00:00:00 2001 From: v-reye Date: Tue, 29 May 2018 10:29:13 -0700 Subject: [PATCH 37/84] Resolved maven build warnings and java warnings regarding deprecated API (#701) * Resolving maven warnings * Removing jreVersion property Does not make sense now that we use final name in the build itself. Only used in 1 place, hard-coding java version for different builds as that's what it represents anyways. * java warnings --- pom.xml | 17 +++++------------ .../sqlserver/jdbc/SQLServerBulkCopy.java | 4 ++-- .../AlwaysEncrypted/CallableStatementTest.java | 14 +++++++------- .../jdbc/bulkCopy/BulkCopyTestUtil.java | 2 +- .../jdbc/connection/ConnectionDriverTest.java | 2 +- .../jdbc/unit/statement/LimitEscapeTest.java | 9 +++------ .../unit/statement/PreparedStatementTest.java | 4 ++-- .../testframework/util/ComparisonUtil.java | 7 ++++++- .../testframework/util/RandomData.java | 7 ++++--- 9 files changed, 31 insertions(+), 35 deletions(-) diff --git a/pom.xml b/pom.xml index 5b39cdd77..f1f5dd160 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.microsoft.sqlserver mssql-jdbc - 6.5.3-SNAPSHOT.${jreVersion}-preview + 6.5.3-SNAPSHOT jar Microsoft JDBC Driver for SQL Server @@ -130,12 +130,8 @@ build42 - - - jre8 - - + ${project.artifactId}-${project.version}.jre8-preview maven-compiler-plugin @@ -166,13 +162,9 @@ build43 true - - - - jre9 - - + + ${project.artifactId}-${project.version}.jre9-preview maven-compiler-plugin @@ -328,6 +320,7 @@ org.codehaus.mojo versions-maven-plugin + 2.5 true outdated-dependencies.txt diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java index 27c608d30..c5ad97c9a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java @@ -3348,7 +3348,7 @@ private byte[] normalizedValue(JDBCType destJdbcType, longValue = (long) (short) value; break; default: - longValue = new Long((Integer) value); + longValue = Long.valueOf((Integer) value); } return ByteBuffer.allocate(Long.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN).putLong(longValue).array(); @@ -3362,7 +3362,7 @@ private byte[] normalizedValue(JDBCType destJdbcType, longValue = (long) (short) value; break; case INTEGER: - longValue = new Long((Integer) value); + longValue = Long.valueOf((Integer) value); break; default: longValue = (long) value; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/CallableStatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/CallableStatementTest.java index bc0c8f173..bb9ef457d 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/CallableStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/CallableStatementTest.java @@ -1904,25 +1904,25 @@ else if (coercion == String.class) { return callableStatement.getString(ordinal); } else if (coercion == Boolean.class) { - return new Boolean(callableStatement.getBoolean(ordinal)); + return Boolean.valueOf(callableStatement.getBoolean(ordinal)); } else if (coercion == Byte.class) { - return new Byte(callableStatement.getByte(ordinal)); + return Byte.valueOf(callableStatement.getByte(ordinal)); } else if (coercion == Short.class) { - return new Short(callableStatement.getShort(ordinal)); + return Short.valueOf(callableStatement.getShort(ordinal)); } else if (coercion == Integer.class) { - return new Integer(callableStatement.getInt(ordinal)); + return Integer.valueOf(callableStatement.getInt(ordinal)); } else if (coercion == Long.class) { - return new Long(callableStatement.getLong(ordinal)); + return Long.valueOf(callableStatement.getLong(ordinal)); } else if (coercion == Float.class) { - return new Float(callableStatement.getFloat(ordinal)); + return Float.valueOf(callableStatement.getFloat(ordinal)); } else if (coercion == Double.class) { - return new Double(callableStatement.getDouble(ordinal)); + return Double.valueOf(callableStatement.getDouble(ordinal)); } else if (coercion == BigDecimal.class) { return callableStatement.getBigDecimal(ordinal); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyTestUtil.java b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyTestUtil.java index 0b902587e..bfdd83cb5 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyTestUtil.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyTestUtil.java @@ -380,7 +380,7 @@ static void validateValues( if(srcValue.getClass().getName().equalsIgnoreCase("java.lang.Double")){ // in case of SQL Server type Float (ie java type double), in float(n) if n is <=24 ie precsion is <=7 SQL Server type Real is returned(ie java type float) if(destMeta.getPrecision(i) <8) - srcValue = new Float(((Double)srcValue)); + srcValue = ((Double)srcValue).floatValue(); } dstValue = dstResultSet.getObject(i); int dstType = destMeta.getColumnType(i); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionDriverTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionDriverTest.java index 3de43f7c1..64a982e6a 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionDriverTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionDriverTest.java @@ -68,7 +68,7 @@ public void testConnectionDriver() throws SQLException { for (DriverPropertyInfo anInfoArray1 : infoArray) { logger.fine(anInfoArray1.name); logger.fine(anInfoArray1.description); - logger.fine(new Boolean(anInfoArray1.required).toString()); + logger.fine(Boolean.valueOf(anInfoArray1.required).toString()); logger.fine(anInfoArray1.value); } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/LimitEscapeTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/LimitEscapeTest.java index 6e3563101..0453b5df1 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/LimitEscapeTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/LimitEscapeTest.java @@ -115,16 +115,13 @@ public void verifyTranslation() throws Exception { cArg[0] = String.class; Class innerClass = Class.forName("com.microsoft.sqlserver.jdbc.JDBCSyntaxTranslator"); Constructor ctor = innerClass.getDeclaredConstructor(); - if (!ctor.isAccessible()) { - ctor.setAccessible(true); - } + ctor.setAccessible(true); Object innerInstance = ctor.newInstance(); Method method = innerClass.getDeclaredMethod("translate", cArg); - if (!method.isAccessible()) { - method.setAccessible(true); - } + + method.setAccessible(true); Object str = method.invoke(innerInstance, inputSql); assertEquals(str, outputSql, "Syntax tyranslation does not match for query: " + queryID); } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java index e4e0d09f0..f64e6d570 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java @@ -317,7 +317,7 @@ public void testStatementPoolingEviction() throws SQLException { // Add new statements to fill up the statement pool. for (int i = 0; i < cacheSize; ++i) { - try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement)con.prepareStatement(query + new Integer(i).toString())) { + try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement)con.prepareStatement(query + String.valueOf(i))) { pstmt.execute(); // sp_executesql pstmt.execute(); // sp_prepexec, actual handle created and cached. } @@ -332,7 +332,7 @@ public void testStatementPoolingEviction() throws SQLException { // (new statement pushes existing statement from pool into discard // action queue). for (int i = cacheSize; i < cacheSize + 5; ++i) { - try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement)con.prepareStatement(query + new Integer(i).toString())) { + try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement)con.prepareStatement(query + String.valueOf(i))) { pstmt.execute(); // sp_executesql pstmt.execute(); // sp_prepexec, actual handle created and cached. } diff --git a/src/test/java/com/microsoft/sqlserver/testframework/util/ComparisonUtil.java b/src/test/java/com/microsoft/sqlserver/testframework/util/ComparisonUtil.java index b297a3924..b251e92bd 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/util/ComparisonUtil.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/util/ComparisonUtil.java @@ -11,6 +11,7 @@ import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; +import java.util.Calendar; import com.microsoft.sqlserver.jdbc.SQLServerResultSetMetaData; import com.microsoft.sqlserver.testframework.DBConnection; @@ -143,7 +144,11 @@ public static void compareExpectedAndActual(int dataType, break; case java.sql.Types.DATE: - assertTrue((((Date) expectedValue).getDate() == (((Date) actualValue).getDate())), "Unexpected datetime value"); + Calendar expC = Calendar.getInstance(); + expC.setTime((Date)expectedValue); + Calendar actC = Calendar.getInstance(); + actC.setTime((Date)actualValue); + assertTrue(expC.get(Calendar.DAY_OF_MONTH) == actC.get(Calendar.DAY_OF_MONTH), "Unexpected datetime value"); break; case java.sql.Types.TIME: diff --git a/src/test/java/com/microsoft/sqlserver/testframework/util/RandomData.java b/src/test/java/com/microsoft/sqlserver/testframework/util/RandomData.java index 21e12991b..e32061109 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/util/RandomData.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/util/RandomData.java @@ -2,6 +2,7 @@ import java.math.BigDecimal; import java.math.BigInteger; +import java.math.RoundingMode; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; @@ -168,7 +169,7 @@ public static BigDecimal generateDecimalNumeric(int precision, if (r.nextBoolean()) { n = BigInteger.TEN.pow(precision); if (scale > 0) - return new BigDecimal(n, scale).subtract(new BigDecimal("" + Math.pow(10, -scale)).setScale(scale, BigDecimal.ROUND_HALF_UP)) + return new BigDecimal(n, scale).subtract(new BigDecimal("" + Math.pow(10, -scale)).setScale(scale, RoundingMode.HALF_UP)) .negate(); else return new BigDecimal(n, scale).subtract(new BigDecimal("1")).negate(); @@ -176,7 +177,7 @@ public static BigDecimal generateDecimalNumeric(int precision, else { n = BigInteger.TEN.pow(precision); if (scale > 0) - return new BigDecimal(n, scale).subtract(new BigDecimal("" + Math.pow(10, -scale)).setScale(scale, BigDecimal.ROUND_HALF_UP)) + return new BigDecimal(n, scale).subtract(new BigDecimal("" + Math.pow(10, -scale)).setScale(scale, RoundingMode.HALF_UP)) .negate(); else return new BigDecimal(n, scale).subtract(new BigDecimal("1")).negate(); @@ -226,7 +227,7 @@ public static Double generateFloat(Integer n, } if (returnZero) { - return new Double(0); + return Double.valueOf(0); } // only 2 options: 24 or 53 From d80908efd11fffe2ddea378f583291db11cce956 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 29 May 2018 13:17:20 -0700 Subject: [PATCH 38/84] make bamoo fixes --- .../jdbc/SQLServerPreparedStatement.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index d2e195379..5f047bfd5 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -113,11 +113,23 @@ private void setPreparedStatementHandle(int handle) { */ private boolean useBulkCopyForBatchInsertOnDW; - public boolean getUseBulkCopyForBatchInsertOnDW() { + /** Sets the prepared statement's useBulkCopyForBatchInsertOnDW value. + * + * @return + * Per the description. + * @throws SQLServerException when an error occurs + */ + public boolean getUseBulkCopyForBatchInsertOnDW() throws SQLServerException { + checkClosed(); return useBulkCopyForBatchInsertOnDW; } - public void setUseBulkCopyForBatchInsertOnDW(boolean useBulkCopyForBatchInsertOnDW) { + /** Fetches the prepared statement's useBulkCopyForBatchInsertOnDW value. + * + * @throws SQLServerException when an error occurs + */ + public void setUseBulkCopyForBatchInsertOnDW(boolean useBulkCopyForBatchInsertOnDW) throws SQLServerException { + checkClosed(); this.useBulkCopyForBatchInsertOnDW = useBulkCopyForBatchInsertOnDW; } From 4f1b3e12012424f5d94a10c448a34aa70fed1e8c Mon Sep 17 00:00:00 2001 From: v-susanh Date: Tue, 29 May 2018 15:25:14 -0700 Subject: [PATCH 39/84] resource bundle for junit test error strings (#698) resource bundle for error message strings in junit tests --- .gitattributes | 33 +- .../jdbc/AlwaysEncrypted/AESetup.java | 5 +- .../CallableStatementTest.java | 380 +++++++++--------- .../JDBCEncryptionDecryptionTest.java | 55 ++- .../AlwaysEncrypted/PrecisionScaleTest.java | 20 +- .../microsoft/sqlserver/jdbc/JDBC43Test.java | 30 +- .../sqlserver/jdbc/TestResource.java | 172 ++++++++ .../bulkCopy/BulkCopyColumnMappingTest.java | 10 +- .../jdbc/bulkCopy/BulkCopyConnectionTest.java | 4 +- .../jdbc/bulkCopy/BulkCopyTestUtil.java | 7 +- .../ISQLServerBulkRecordIssuesTest.java | 21 +- .../microsoft/sqlserver/jdbc/bvt/bvtTest.java | 19 +- .../CallableStatementTest.java | 12 +- .../jdbc/connection/ConnectionDriverTest.java | 122 +++--- .../connection/NativeMSSQLDataSourceTest.java | 3 +- .../jdbc/connection/PoolingTest.java | 13 +- .../jdbc/connection/SSLProtocolTest.java | 15 +- .../jdbc/connection/TimeoutTest.java | 35 +- .../jdbc/connection/WarningTest.java | 7 +- .../DatabaseMetaDataForeignKeyTest.java | 21 +- .../DatabaseMetaDataTest.java | 122 +++--- .../SQLServerSpatialDatatypeTest.java | 18 +- .../datatypes/SQLVariantResultSetTest.java | 3 +- .../jdbc/datatypes/TVPWithSqlVariantTest.java | 6 +- .../jdbc/exception/ExceptionTest.java | 16 +- .../sqlserver/jdbc/fips/FipsEnvTest.java | 22 +- .../sqlserver/jdbc/fips/FipsTest.java | 26 +- .../BatchExecutionWithNullTest.java | 9 +- .../preparedStatement/RegressionTest.java | 13 +- .../sqlserver/jdbc/tvp/TVPIssuesTest.java | 10 +- .../jdbc/tvp/TVPResultSetCursorTest.java | 7 +- .../sqlserver/jdbc/tvp/TVPSchemaTest.java | 15 +- .../sqlserver/jdbc/unit/TestSavepoint.java | 29 +- .../sqlserver/jdbc/unit/lobs/lobsTest.java | 22 +- .../statement/BatchExecuteWithErrorsTest.java | 60 +-- .../unit/statement/BatchExecutionTest.java | 29 +- .../jdbc/unit/statement/BatchTriggerTest.java | 14 +- .../unit/statement/CallableMixedTest.java | 13 +- .../jdbc/unit/statement/LimitEscapeTest.java | 15 +- .../jdbc/unit/statement/MergeTest.java | 3 +- .../statement/NamedParamMultiPartTest.java | 13 +- .../jdbc/unit/statement/PQImpsTest.java | 39 +- .../jdbc/unit/statement/PoolableTest.java | 13 +- .../unit/statement/PreparedStatementTest.java | 5 +- .../jdbc/unit/statement/RegressionTest.java | 11 +- .../statement/StatementCancellationTest.java | 10 +- .../jdbc/unit/statement/StatementTest.java | 201 +++++---- .../jdbc/unit/statement/WrapperTest.java | 31 +- 48 files changed, 1009 insertions(+), 750 deletions(-) create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java diff --git a/.gitattributes b/.gitattributes index eb5d74682..0abcfbee4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,33 +1,2 @@ # Auto detect text files and perform LF normalization -* text=auto - -# -# The above will handle all files NOT found below -# -# These files are text and should be normalized (Convert crlf => lf) -*.css text -*.md text -*.htm text -*.html text -*.java text -*.js text -*.json text -*.properties text -*.sh text -*.svg text -*.txt text -*.xml text - -# These files are binary and should be left untouched -# (binary is a macro for -text -diff) -*.class binary -*.dll binary -*.ear binary -*.gif binary -*.ico binary -*.jar binary -*.jpg binary -*.jpeg binary -*.png binary -*.so binary -*.war binary +* -text diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java index 0c31108ac..2bb77e477 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java @@ -34,6 +34,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; import com.microsoft.sqlserver.jdbc.SQLServerStatement; import com.microsoft.sqlserver.jdbc.SQLServerStatementColumnEncryptionSetting; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBConnection; import com.microsoft.sqlserver.testframework.Utils; @@ -87,7 +88,7 @@ public class AESetup extends AbstractTest { @BeforeAll static void setUpConnection() throws TestAbortedException, Exception { assumeTrue(13 <= new DBConnection(connectionString).getServerVersion(), - "Aborting test case as SQL Server version is not compatible with Always encrypted "); + TestResource.getResource("R_Incompat_SQLServerVersion")); String AETestConenctionString = connectionString + ";sendTimeAsDateTime=false"; readFromFile(javaKeyStoreInputFile, "Alias name"); @@ -140,7 +141,7 @@ private static void readFromFile(String inputFile, filePath = Utils.getCurrentClassPath(); try { File f = new File(filePath + inputFile); - assumeTrue(f.exists(), "Aborting test case since no java key store and alias name exists!"); + assumeTrue(f.exists(), TestResource.getResource("R_noKeyStore")); try(BufferedReader buffer = new BufferedReader(new FileReader(f))) { String readLine = ""; String[] linecontents; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/CallableStatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/CallableStatementTest.java index bb9ef457d..8ab4ec012 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/CallableStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/CallableStatementTest.java @@ -28,6 +28,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerCallableStatement; import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; import com.microsoft.sqlserver.jdbc.SQLServerResultSet; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.Utils; import com.microsoft.sqlserver.testframework.util.RandomData; import com.microsoft.sqlserver.testframework.util.Util; @@ -696,7 +697,7 @@ private void testInputProcedure(String sql, try (SQLServerResultSet rs = (SQLServerResultSet) callableStatement.executeQuery()) { rs.next(); - assertEquals(rs.getString(1), values[3], "" + "Test for input parameter fails.\n"); + assertEquals(rs.getString(1), values[3], "" + TestResource.getResource("R_inputParamFailed")); } } catch (Exception e) { @@ -734,14 +735,14 @@ private void testInputProcedure2(String sql) throws SQLException { try (SQLServerResultSet rs = (SQLServerResultSet) callableStatement.executeQuery()) { rs.next(); - assertEquals(rs.getString(1).trim(), charValues[1], "Test for input parameter fails.\n"); - assertEquals(rs.getUniqueIdentifier(2), charValues[6].toUpperCase(), "Test for input parameter fails.\n"); - assertEquals(rs.getString(3).trim(), charValues[2], "Test for input parameter fails.\n"); - assertEquals(rs.getString(4).trim(), charValues[3], "Test for input parameter fails.\n"); - assertEquals(rs.getString(5).trim(), charValues[4], "Test for input parameter fails.\n"); - assertEquals(rs.getString(6).trim(), charValues[5], "Test for input parameter fails.\n"); - assertEquals(rs.getString(7).trim(), charValues[7], "Test for input parameter fails.\n"); - assertEquals(rs.getString(8).trim(), charValues[8], "Test for input parameter fails.\n"); + assertEquals(rs.getString(1).trim(), charValues[1], TestResource.getResource("R_inputParamFailed")); + assertEquals(rs.getUniqueIdentifier(2), charValues[6].toUpperCase(), TestResource.getResource("R_inputParamFailed")); + assertEquals(rs.getString(3).trim(), charValues[2], TestResource.getResource("R_inputParamFailed")); + assertEquals(rs.getString(4).trim(), charValues[3], TestResource.getResource("R_inputParamFailed")); + assertEquals(rs.getString(5).trim(), charValues[4], TestResource.getResource("R_inputParamFailed")); + assertEquals(rs.getString(6).trim(), charValues[5], TestResource.getResource("R_inputParamFailed")); + assertEquals(rs.getString(7).trim(), charValues[7], TestResource.getResource("R_inputParamFailed")); + assertEquals(rs.getString(8).trim(), charValues[8], TestResource.getResource("R_inputParamFailed")); } } catch (Exception e) { @@ -770,19 +771,19 @@ private void testOutputProcedure3RandomOrder(String sql) throws SQLException { callableStatement.execute(); int intValue2 = callableStatement.getInt(2); - assertEquals("" + intValue2, numericValues[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue2, numericValues[3], TestResource.getResource("R_outputParamFailed")); int intValue = callableStatement.getInt(1); - assertEquals("" + intValue, numericValues[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue, numericValues[3], TestResource.getResource("R_outputParamFailed")); int intValue3 = callableStatement.getInt(2); - assertEquals("" + intValue3, numericValues[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue3, numericValues[3], TestResource.getResource("R_outputParamFailed")); int intValue4 = callableStatement.getInt(2); - assertEquals("" + intValue4, numericValues[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue4, numericValues[3], TestResource.getResource("R_outputParamFailed")); int intValue5 = callableStatement.getInt(1); - assertEquals("" + intValue5, numericValues[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue5, numericValues[3], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { fail(e.toString()); @@ -799,10 +800,10 @@ private void testOutputProcedure3Inorder(String sql) throws SQLException { callableStatement.execute(); int intValue = callableStatement.getInt(1); - assertEquals("" + intValue, numericValues[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue, numericValues[3], TestResource.getResource("R_outputParamFailed")); int intValue2 = callableStatement.getInt(2); - assertEquals("" + intValue2, numericValues[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue2, numericValues[3], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { fail(e.toString()); @@ -819,10 +820,10 @@ private void testOutputProcedure3ReverseOrder(String sql) throws SQLException { callableStatement.execute(); int intValue2 = callableStatement.getInt(2); - assertEquals("" + intValue2, numericValues[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue2, numericValues[3], TestResource.getResource("R_outputParamFailed")); int intValue = callableStatement.getInt(1); - assertEquals("" + intValue, numericValues[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue, numericValues[3], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { fail(e.toString()); @@ -863,34 +864,34 @@ private void testOutputProcedure2RandomOrder(String sql, callableStatement.execute(); BigDecimal ecnryptedSmallMoney = callableStatement.getSmallMoney(7); - assertEquals("" + ecnryptedSmallMoney, values[12], "Test for output parameter fails.\n"); + assertEquals("" + ecnryptedSmallMoney, values[12], TestResource.getResource("R_outputParamFailed")); short encryptedSmallint = callableStatement.getShort(4); - assertEquals("" + encryptedSmallint, values[2], "Test for output parameter fails.\n"); + assertEquals("" + encryptedSmallint, values[2], TestResource.getResource("R_outputParamFailed")); BigDecimal SmallMoneyValue = callableStatement.getSmallMoney(8); - assertEquals("" + SmallMoneyValue, values[12], "Test for output parameter fails.\n"); + assertEquals("" + SmallMoneyValue, values[12], TestResource.getResource("R_outputParamFailed")); short encryptedTinyint = callableStatement.getShort(6); - assertEquals("" + encryptedTinyint, values[1], "Test for output parameter fails.\n"); + assertEquals("" + encryptedTinyint, values[1], TestResource.getResource("R_outputParamFailed")); short tinyintValue = callableStatement.getShort(5); - assertEquals("" + tinyintValue, values[1], "Test for output parameter fails.\n"); + assertEquals("" + tinyintValue, values[1], TestResource.getResource("R_outputParamFailed")); BigDecimal encryptedMoneyValue = callableStatement.getMoney(9); - assertEquals("" + encryptedMoneyValue, values[13], "Test for output parameter fails.\n"); + assertEquals("" + encryptedMoneyValue, values[13], TestResource.getResource("R_outputParamFailed")); short smallintValue = callableStatement.getShort(3); - assertEquals("" + smallintValue, values[2], "Test for output parameter fails.\n"); + assertEquals("" + smallintValue, values[2], TestResource.getResource("R_outputParamFailed")); int intValue = callableStatement.getInt(1); - assertEquals("" + intValue, values[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue, values[3], TestResource.getResource("R_outputParamFailed")); BigDecimal encryptedSmallMoney = callableStatement.getMoney(10); - assertEquals("" + encryptedSmallMoney, values[13], "Test for output parameter fails.\n"); + assertEquals("" + encryptedSmallMoney, values[13], TestResource.getResource("R_outputParamFailed")); int encryptedInt = callableStatement.getInt(2); - assertEquals("" + encryptedInt, values[3], "Test for output parameter fails.\n"); + assertEquals("" + encryptedInt, values[3], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { fail(e.toString()); @@ -915,34 +916,34 @@ private void testOutputProcedure2Inorder(String sql, callableStatement.execute(); int intValue = callableStatement.getInt(1); - assertEquals("" + intValue, values[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue, values[3], TestResource.getResource("R_outputParamFailed")); int encryptedInt = callableStatement.getInt(2); - assertEquals("" + encryptedInt, values[3], "Test for output parameter fails.\n"); + assertEquals("" + encryptedInt, values[3], TestResource.getResource("R_outputParamFailed")); short smallintValue = callableStatement.getShort(3); - assertEquals("" + smallintValue, values[2], "Test for output parameter fails.\n"); + assertEquals("" + smallintValue, values[2], TestResource.getResource("R_outputParamFailed")); short encryptedSmallint = callableStatement.getShort(4); - assertEquals("" + encryptedSmallint, values[2], "Test for output parameter fails.\n"); + assertEquals("" + encryptedSmallint, values[2], TestResource.getResource("R_outputParamFailed")); short tinyintValue = callableStatement.getShort(5); - assertEquals("" + tinyintValue, values[1], "Test for output parameter fails.\n"); + assertEquals("" + tinyintValue, values[1], TestResource.getResource("R_outputParamFailed")); short encryptedTinyint = callableStatement.getShort(6); - assertEquals("" + encryptedTinyint, values[1], "Test for output parameter fails.\n"); + assertEquals("" + encryptedTinyint, values[1], TestResource.getResource("R_outputParamFailed")); BigDecimal encryptedSmallMoney = callableStatement.getSmallMoney(7); - assertEquals("" + encryptedSmallMoney, values[12], "Test for output parameter fails.\n"); + assertEquals("" + encryptedSmallMoney, values[12], TestResource.getResource("R_outputParamFailed")); BigDecimal SmallMoneyValue = callableStatement.getSmallMoney(8); - assertEquals("" + SmallMoneyValue, values[12], "Test for output parameter fails.\n"); + assertEquals("" + SmallMoneyValue, values[12], TestResource.getResource("R_outputParamFailed")); BigDecimal MoneyValue = callableStatement.getMoney(9); - assertEquals("" + MoneyValue, values[13], "Test for output parameter fails.\n"); + assertEquals("" + MoneyValue, values[13], TestResource.getResource("R_outputParamFailed")); BigDecimal encryptedMoney = callableStatement.getMoney(10); - assertEquals("" + encryptedMoney, values[13], "Test for output parameter fails.\n"); + assertEquals("" + encryptedMoney, values[13], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { @@ -969,34 +970,33 @@ private void testOutputProcedure2ReverseOrder(String sql, callableStatement.execute(); BigDecimal encryptedMoney = callableStatement.getMoney(10); - assertEquals("" + encryptedMoney, values[13], "Test for output parameter fails.\n"); + assertEquals("" + encryptedMoney, values[13], TestResource.getResource("R_outputParamFailed")); BigDecimal MoneyValue = callableStatement.getMoney(9); - assertEquals("" + MoneyValue, values[13], "Test for output parameter fails.\n"); + assertEquals("" + MoneyValue, values[13], TestResource.getResource("R_outputParamFailed")); BigDecimal SmallMoneyValue = callableStatement.getSmallMoney(8); - assertEquals("" + SmallMoneyValue, values[12], "Test for output parameter fails.\n"); + assertEquals("" + SmallMoneyValue, values[12], TestResource.getResource("R_outputParamFailed")); BigDecimal encryptedSmallMoney = callableStatement.getSmallMoney(7); - assertEquals("" + encryptedSmallMoney, values[12], "Test for output parameter fails.\n"); + assertEquals("" + encryptedSmallMoney, values[12], TestResource.getResource("R_outputParamFailed")); short encryptedTinyint = callableStatement.getShort(6); - assertEquals("" + encryptedTinyint, values[1], "Test for output parameter fails.\n"); short tinyintValue = callableStatement.getShort(5); - assertEquals("" + tinyintValue, values[1], "Test for output parameter fails.\n"); + assertEquals("" + tinyintValue, values[1], TestResource.getResource("R_outputParamFailed")); short encryptedSmallint = callableStatement.getShort(4); - assertEquals("" + encryptedSmallint, values[2], "Test for output parameter fails.\n"); + assertEquals("" + encryptedSmallint, values[2], TestResource.getResource("R_outputParamFailed")); short smallintValue = callableStatement.getShort(3); - assertEquals("" + smallintValue, values[2], "Test for output parameter fails.\n"); + assertEquals("" + smallintValue, values[2], TestResource.getResource("R_outputParamFailed")); int encryptedInt = callableStatement.getInt(2); - assertEquals("" + encryptedInt, values[3], "Test for output parameter fails.\n"); + assertEquals("" + encryptedInt, values[3], TestResource.getResource("R_outputParamFailed")); int intValue = callableStatement.getInt(1); - assertEquals("" + intValue, values[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue, values[3], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { @@ -1033,33 +1033,33 @@ private void testOutputProcedureRandomOrder(String sql, callableStatement.execute(); double floatValue0 = callableStatement.getDouble(2); - assertEquals("" + floatValue0, "" + values[5], "Test for output parameter fails.\n"); + assertEquals("" + floatValue0, "" + values[5], TestResource.getResource("R_outputParamFailed")); long bigintValue = callableStatement.getLong(4); - assertEquals("" + bigintValue, values[4], "Test for output parameter fails.\n"); + assertEquals("" + bigintValue, values[4], TestResource.getResource("R_outputParamFailed")); short tinyintValue = callableStatement.getShort(5); // tinyint - assertEquals("" + tinyintValue, values[1], "Test for output parameter fails.\n"); + assertEquals("" + tinyintValue, values[1], TestResource.getResource("R_outputParamFailed")); double floatValue1 = callableStatement.getDouble(2); - assertEquals("" + floatValue1, "" + values[5], "Test for output parameter fails.\n"); + assertEquals("" + floatValue1, "" + values[5], TestResource.getResource("R_outputParamFailed")); int intValue2 = callableStatement.getInt(1); - assertEquals("" + intValue2, "" + values[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue2, "" + values[3], TestResource.getResource("R_outputParamFailed")); double floatValue2 = callableStatement.getDouble(2); - assertEquals("" + floatValue2, "" + values[5], "Test for output parameter fails.\n"); + assertEquals("" + floatValue2, "" + values[5], TestResource.getResource("R_outputParamFailed")); short shortValue3 = callableStatement.getShort(3); // smallint - assertEquals("" + shortValue3, "" + values[2], "Test for output parameter fails.\n"); + assertEquals("" + shortValue3, "" + values[2], TestResource.getResource("R_outputParamFailed")); short shortValue32 = callableStatement.getShort(3); - assertEquals("" + shortValue32, "" + values[2], "Test for output parameter fails.\n"); + assertEquals("" + shortValue32, "" + values[2], TestResource.getResource("R_outputParamFailed")); BigDecimal smallmoney1 = callableStatement.getSmallMoney(6); - assertEquals("" + smallmoney1, "" + values[12], "Test for output parameter fails.\n"); + assertEquals("" + smallmoney1, "" + values[12], TestResource.getResource("R_outputParamFailed")); BigDecimal money1 = callableStatement.getMoney(7); - assertEquals("" + money1, "" + values[13], "Test for output parameter fails.\n"); + assertEquals("" + money1, "" + values[13], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { fail(e.toString()); @@ -1082,25 +1082,25 @@ private void testOutputProcedureInorder(String sql, callableStatement.execute(); int intValue2 = callableStatement.getInt(1); - assertEquals("" + intValue2, values[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue2, values[3], TestResource.getResource("R_outputParamFailed")); double floatValue0 = callableStatement.getDouble(2); - assertEquals("" + floatValue0, values[5], "Test for output parameter fails.\n"); + assertEquals("" + floatValue0, values[5], TestResource.getResource("R_outputParamFailed")); short shortValue3 = callableStatement.getShort(3); - assertEquals("" + shortValue3, values[2], "Test for output parameter fails.\n"); + assertEquals("" + shortValue3, values[2], TestResource.getResource("R_outputParamFailed")); long bigintValue = callableStatement.getLong(4); - assertEquals("" + bigintValue, values[4], "Test for output parameter fails.\n"); + assertEquals("" + bigintValue, values[4], TestResource.getResource("R_outputParamFailed")); short tinyintValue = callableStatement.getShort(5); - assertEquals("" + tinyintValue, values[1], "Test for output parameter fails.\n"); + assertEquals("" + tinyintValue, values[1], TestResource.getResource("R_outputParamFailed")); BigDecimal smallMoney1 = callableStatement.getSmallMoney(6); - assertEquals("" + smallMoney1, values[12], "Test for output parameter fails.\n"); + assertEquals("" + smallMoney1, values[12], TestResource.getResource("R_outputParamFailed")); BigDecimal money1 = callableStatement.getMoney(7); - assertEquals("" + money1, values[13], "Test for output parameter fails.\n"); + assertEquals("" + money1, values[13], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { @@ -1123,25 +1123,25 @@ private void testOutputProcedureReverseOrder(String sql, callableStatement.execute(); BigDecimal smallMoney1 = callableStatement.getSmallMoney(6); - assertEquals("" + smallMoney1, values[12], "Test for output parameter fails.\n"); + assertEquals("" + smallMoney1, values[12], TestResource.getResource("R_outputParamFailed")); BigDecimal money1 = callableStatement.getMoney(7); - assertEquals("" + money1, values[13], "Test for output parameter fails.\n"); + assertEquals("" + money1, values[13], TestResource.getResource("R_outputParamFailed")); short tinyintValue = callableStatement.getShort(5); - assertEquals("" + tinyintValue, values[1], "Test for output parameter fails.\n"); + assertEquals("" + tinyintValue, values[1], TestResource.getResource("R_outputParamFailed")); long bigintValue = callableStatement.getLong(4); - assertEquals("" + bigintValue, values[4], "Test for output parameter fails.\n"); + assertEquals("" + bigintValue, values[4], TestResource.getResource("R_outputParamFailed")); short shortValue3 = callableStatement.getShort(3); - assertEquals("" + shortValue3, values[2], "Test for output parameter fails.\n"); + assertEquals("" + shortValue3, values[2], TestResource.getResource("R_outputParamFailed")); double floatValue0 = callableStatement.getDouble(2); - assertEquals("" + floatValue0, values[5], "Test for output parameter fails.\n"); + assertEquals("" + floatValue0, values[5], TestResource.getResource("R_outputParamFailed")); int intValue2 = callableStatement.getInt(1); - assertEquals("" + intValue2, values[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue2, values[3], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { @@ -1207,7 +1207,7 @@ private void testMixedProcedure(String sql) throws SQLException { assertEquals("" + intValue, numericValues[3], "Test for Inout parameter fails.\n"); double floatValue = callableStatement.getDouble(3); - assertEquals("" + floatValue, numericValues[5], "Test for output parameter fails.\n"); + assertEquals("" + floatValue, numericValues[5], TestResource.getResource("R_outputParamFailed")); int returnedValue = callableStatement.getInt(1); assertEquals("" + returnedValue, "" + 123, "Test for Inout parameter fails.\n"); @@ -1240,22 +1240,22 @@ private void testMixedProcedure2RandomOrder(String sql) throws SQLException { callableStatement.execute(); double floatValue = callableStatement.getDouble(2); - assertEquals("" + floatValue, numericValues[5], "Test for output parameter fails.\n"); + assertEquals("" + floatValue, numericValues[5], TestResource.getResource("R_outputParamFailed")); int intValue = callableStatement.getInt(1); - assertEquals("" + intValue, numericValues[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue, numericValues[3], TestResource.getResource("R_outputParamFailed")); double floatValue2 = callableStatement.getDouble(2); - assertEquals("" + floatValue2, numericValues[5], "Test for output parameter fails.\n"); + assertEquals("" + floatValue2, numericValues[5], TestResource.getResource("R_outputParamFailed")); int intValue2 = callableStatement.getInt(1); - assertEquals("" + intValue2, numericValues[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue2, numericValues[3], TestResource.getResource("R_outputParamFailed")); int intValue3 = callableStatement.getInt(1); - assertEquals("" + intValue3, numericValues[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue3, numericValues[3], TestResource.getResource("R_outputParamFailed")); double floatValue3 = callableStatement.getDouble(2); - assertEquals("" + floatValue3, numericValues[5], "Test for output parameter fails.\n"); + assertEquals("" + floatValue3, numericValues[5], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { @@ -1274,10 +1274,10 @@ private void testMixedProcedure2Inorder(String sql) throws SQLException { callableStatement.execute(); int intValue = callableStatement.getInt(1); - assertEquals("" + intValue, numericValues[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue, numericValues[3], TestResource.getResource("R_outputParamFailed")); double floatValue = callableStatement.getDouble(2); - assertEquals("" + floatValue, numericValues[5], "Test for output parameter fails.\n"); + assertEquals("" + floatValue, numericValues[5], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { fail(e.toString()); @@ -1306,22 +1306,22 @@ private void testMixedProcedure3RandomOrder(String sql) throws SQLException { callableStatement.execute(); double floatValue = callableStatement.getDouble(2); - assertEquals("" + floatValue, numericValues[5], "Test for output parameter fails.\n"); + assertEquals("" + floatValue, numericValues[5], TestResource.getResource("R_outputParamFailed")); long bigintValue = callableStatement.getLong(1); - assertEquals("" + bigintValue, numericValues[4], "Test for output parameter fails.\n"); + assertEquals("" + bigintValue, numericValues[4], TestResource.getResource("R_outputParamFailed")); long bigintValue1 = callableStatement.getLong(1); - assertEquals("" + bigintValue1, numericValues[4], "Test for output parameter fails.\n"); + assertEquals("" + bigintValue1, numericValues[4], TestResource.getResource("R_outputParamFailed")); double floatValue2 = callableStatement.getDouble(2); - assertEquals("" + floatValue2, numericValues[5], "Test for output parameter fails.\n"); + assertEquals("" + floatValue2, numericValues[5], TestResource.getResource("R_outputParamFailed")); double floatValue3 = callableStatement.getDouble(2); - assertEquals("" + floatValue3, numericValues[5], "Test for output parameter fails.\n"); + assertEquals("" + floatValue3, numericValues[5], TestResource.getResource("R_outputParamFailed")); long bigintValue3 = callableStatement.getLong(1); - assertEquals("" + bigintValue3, numericValues[4], "Test for output parameter fails.\n"); + assertEquals("" + bigintValue3, numericValues[4], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { @@ -1340,10 +1340,10 @@ private void testMixedProcedure3Inorder(String sql) throws SQLException { callableStatement.execute(); long bigintValue = callableStatement.getLong(1); - assertEquals("" + bigintValue, numericValues[4], "Test for output parameter fails.\n"); + assertEquals("" + bigintValue, numericValues[4], TestResource.getResource("R_outputParamFailed")); double floatValue = callableStatement.getDouble(2); - assertEquals("" + floatValue, numericValues[5], "Test for output parameter fails.\n"); + assertEquals("" + floatValue, numericValues[5], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { fail(e.toString()); @@ -1361,10 +1361,10 @@ private void testMixedProcedure3ReverseOrder(String sql) throws SQLException { callableStatement.execute(); double floatValue = callableStatement.getDouble(2); - assertEquals("" + floatValue, numericValues[5], "Test for output parameter fails.\n"); + assertEquals("" + floatValue, numericValues[5], TestResource.getResource("R_outputParamFailed")); long bigintValue = callableStatement.getLong(1); - assertEquals("" + bigintValue, numericValues[4], "Test for output parameter fails.\n"); + assertEquals("" + bigintValue, numericValues[4], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { fail(e.toString()); @@ -1474,31 +1474,31 @@ private void testOutputProcedureCharInorder(String sql) throws SQLException { callableStatement.execute(); String charValue = callableStatement.getString(1).trim(); - assertEquals(charValue, charValues[0], "Test for output parameter fails.\n"); + assertEquals(charValue, charValues[0], TestResource.getResource("R_outputParamFailed")); String varcharValue = callableStatement.getString(2).trim(); - assertEquals(varcharValue, charValues[1], "Test for output parameter fails.\n"); + assertEquals(varcharValue, charValues[1], TestResource.getResource("R_outputParamFailed")); String ncharValue = callableStatement.getString(3).trim(); - assertEquals(ncharValue, charValues[3], "Test for output parameter fails.\n"); + assertEquals(ncharValue, charValues[3], TestResource.getResource("R_outputParamFailed")); String nvarcharValue = callableStatement.getString(4).trim(); - assertEquals(nvarcharValue, charValues[4], "Test for output parameter fails.\n"); + assertEquals(nvarcharValue, charValues[4], TestResource.getResource("R_outputParamFailed")); String uniqueIdentifierValue = callableStatement.getString(5).trim(); - assertEquals(uniqueIdentifierValue.toLowerCase(), charValues[6], "Test for output parameter fails.\n"); + assertEquals(uniqueIdentifierValue.toLowerCase(), charValues[6], TestResource.getResource("R_outputParamFailed")); String varcharValuemax = callableStatement.getString(6).trim(); - assertEquals(varcharValuemax, charValues[2], "Test for output parameter fails.\n"); + assertEquals(varcharValuemax, charValues[2], TestResource.getResource("R_outputParamFailed")); String nvarcharValuemax = callableStatement.getString(7).trim(); - assertEquals(nvarcharValuemax, charValues[5], "Test for output parameter fails.\n"); + assertEquals(nvarcharValuemax, charValues[5], TestResource.getResource("R_outputParamFailed")); String varcharValue8000 = callableStatement.getString(8).trim(); - assertEquals(varcharValue8000, charValues[7], "Test for output parameter fails.\n"); + assertEquals(varcharValue8000, charValues[7], TestResource.getResource("R_outputParamFailed")); String nvarcharValue4000 = callableStatement.getNString(9).trim(); - assertEquals(nvarcharValue4000, charValues[8], "Test for output parameter fails.\n"); + assertEquals(nvarcharValue4000, charValues[8], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { @@ -1522,33 +1522,33 @@ private void testOutputProcedureCharInorderObject(String sql) throws SQLExceptio callableStatement.execute(); String charValue = (String) callableStatement.getObject(1); - assertEquals(charValue.trim(), charValues[0], "Test for output parameter fails.\n"); + assertEquals(charValue.trim(), charValues[0], TestResource.getResource("R_outputParamFailed")); String varcharValue = (String) callableStatement.getObject(2); - assertEquals(varcharValue.trim(), charValues[1], "Test for output parameter fails.\n"); + assertEquals(varcharValue.trim(), charValues[1], TestResource.getResource("R_outputParamFailed")); String ncharValue = (String) callableStatement.getObject(3); - assertEquals(ncharValue.trim(), charValues[3], "Test for output parameter fails.\n"); + assertEquals(ncharValue.trim(), charValues[3], TestResource.getResource("R_outputParamFailed")); String nvarcharValue = (String) callableStatement.getObject(4); - assertEquals(nvarcharValue.trim(), charValues[4], "Test for output parameter fails.\n"); + assertEquals(nvarcharValue.trim(), charValues[4], TestResource.getResource("R_outputParamFailed")); String uniqueIdentifierValue = (String) callableStatement.getObject(5); - assertEquals(uniqueIdentifierValue.toLowerCase(), charValues[6], "Test for output parameter fails.\n"); + assertEquals(uniqueIdentifierValue.toLowerCase(), charValues[6], TestResource.getResource("R_outputParamFailed")); String varcharValuemax = (String) callableStatement.getObject(6); - assertEquals(varcharValuemax, charValues[2], "Test for output parameter fails.\n"); + assertEquals(varcharValuemax, charValues[2], TestResource.getResource("R_outputParamFailed")); String nvarcharValuemax = (String) callableStatement.getObject(7); - assertEquals(nvarcharValuemax.trim(), charValues[5], "Test for output parameter fails.\n"); + assertEquals(nvarcharValuemax.trim(), charValues[5], TestResource.getResource("R_outputParamFailed")); String varcharValue8000 = (String) callableStatement.getObject(8); - assertEquals(varcharValue8000, charValues[7], "Test for output parameter fails.\n"); + assertEquals(varcharValue8000, charValues[7], TestResource.getResource("R_outputParamFailed")); String nvarcharValue4000 = (String) callableStatement.getObject(9); - assertEquals(nvarcharValue4000, charValues[8], "Test for output parameter fails.\n"); + assertEquals(nvarcharValue4000, charValues[8], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { @@ -1597,54 +1597,54 @@ private void testOutputProcedureNumericInorder(String sql) throws SQLException { int bitValue = callableStatement.getInt(1); if (bitValue == 0) - assertEquals("" + false, numericValues[0], "Test for output parameter fails.\n"); + assertEquals("" + false, numericValues[0], TestResource.getResource("R_outputParamFailed")); else - assertEquals("" + true, numericValues[0], "Test for output parameter fails.\n"); + assertEquals("" + true, numericValues[0], TestResource.getResource("R_outputParamFailed")); short tinyIntValue = callableStatement.getShort(2); - assertEquals("" + tinyIntValue, numericValues[1], "Test for output parameter fails.\n"); + assertEquals("" + tinyIntValue, numericValues[1], TestResource.getResource("R_outputParamFailed")); short smallIntValue = callableStatement.getShort(3); - assertEquals("" + smallIntValue, numericValues[2], "Test for output parameter fails.\n"); + assertEquals("" + smallIntValue, numericValues[2], TestResource.getResource("R_outputParamFailed")); int intValue = callableStatement.getInt(4); - assertEquals("" + intValue, numericValues[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue, numericValues[3], TestResource.getResource("R_outputParamFailed")); long bigintValue = callableStatement.getLong(5); - assertEquals("" + bigintValue, numericValues[4], "Test for output parameter fails.\n"); + assertEquals("" + bigintValue, numericValues[4], TestResource.getResource("R_outputParamFailed")); double floatDefault = callableStatement.getDouble(6); - assertEquals("" + floatDefault, numericValues[5], "Test for output parameter fails.\n"); + assertEquals("" + floatDefault, numericValues[5], TestResource.getResource("R_outputParamFailed")); double floatValue = callableStatement.getDouble(7); - assertEquals("" + floatValue, numericValues[6], "Test for output parameter fails.\n"); + assertEquals("" + floatValue, numericValues[6], TestResource.getResource("R_outputParamFailed")); float realValue = callableStatement.getFloat(8); - assertEquals("" + realValue, numericValues[7], "Test for output parameter fails.\n"); + assertEquals("" + realValue, numericValues[7], TestResource.getResource("R_outputParamFailed")); BigDecimal decimalDefault = callableStatement.getBigDecimal(9); - assertEquals(decimalDefault, new BigDecimal(numericValues[8]), "Test for output parameter fails.\n"); + assertEquals(decimalDefault, new BigDecimal(numericValues[8]), TestResource.getResource("R_outputParamFailed")); BigDecimal decimalValue = callableStatement.getBigDecimal(10); - assertEquals(decimalValue, new BigDecimal(numericValues[9]), "Test for output parameter fails.\n"); + assertEquals(decimalValue, new BigDecimal(numericValues[9]), TestResource.getResource("R_outputParamFailed")); BigDecimal numericDefault = callableStatement.getBigDecimal(11); - assertEquals(numericDefault, new BigDecimal(numericValues[10]), "Test for output parameter fails.\n"); + assertEquals(numericDefault, new BigDecimal(numericValues[10]), TestResource.getResource("R_outputParamFailed")); BigDecimal numericValue = callableStatement.getBigDecimal(12); - assertEquals(numericValue, new BigDecimal(numericValues[11]), "Test for output parameter fails.\n"); + assertEquals(numericValue, new BigDecimal(numericValues[11]), TestResource.getResource("R_outputParamFailed")); BigDecimal smallMoneyValue = callableStatement.getSmallMoney(13); - assertEquals(smallMoneyValue, new BigDecimal(numericValues[12]), "Test for output parameter fails.\n"); + assertEquals(smallMoneyValue, new BigDecimal(numericValues[12]), TestResource.getResource("R_outputParamFailed")); BigDecimal moneyValue = callableStatement.getMoney(14); - assertEquals(moneyValue, new BigDecimal(numericValues[13]), "Test for output parameter fails.\n"); + assertEquals(moneyValue, new BigDecimal(numericValues[13]), TestResource.getResource("R_outputParamFailed")); BigDecimal decimalValue2 = callableStatement.getBigDecimal(15); - assertEquals(decimalValue2, new BigDecimal(numericValues[14]), "Test for output parameter fails.\n"); + assertEquals(decimalValue2, new BigDecimal(numericValues[14]), TestResource.getResource("R_outputParamFailed")); BigDecimal numericValue2 = callableStatement.getBigDecimal(16); - assertEquals(numericValue2, new BigDecimal(numericValues[15]), "Test for output parameter fails.\n"); + assertEquals(numericValue2, new BigDecimal(numericValues[15]), TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { @@ -1683,7 +1683,7 @@ private void testcoerctionsOutputProcedureNumericInorder(String sql) throws SQLE boolVal = true; else if (value.toString().equals("0") || value.equals(false) || value.toString().equals("0.0")) boolVal = false; - assertEquals("" + boolVal, numericValues[0], "Test for output parameter fails.\n"); + assertEquals("" + boolVal, numericValues[0], TestResource.getResource("R_outputParamFailed")); } Class[] tinyint_coercions = {Object.class, Short.class, Integer.class, Long.class, Float.class, Double.class, BigDecimal.class, String.class}; @@ -1693,9 +1693,9 @@ else if (value.toString().equals("0") || value.equals(false) || value.toString() Object x = createValue(tinyint_coercions[i], 1); if (x instanceof String) - assertEquals("" + tinyIntValue, x, "Test for output parameter fails.\n"); + assertEquals("" + tinyIntValue, x, TestResource.getResource("R_outputParamFailed")); else - assertEquals(tinyIntValue, x, "Test for output parameter fails.\n"); + assertEquals(tinyIntValue, x, TestResource.getResource("R_outputParamFailed")); } Class[] smallint_coercions = {Object.class, Short.class, Integer.class, Long.class, Float.class, Double.class, BigDecimal.class, @@ -1705,9 +1705,9 @@ else if (value.toString().equals("0") || value.equals(false) || value.toString() Object x = createValue(smallint_coercions[i], 2); if (x instanceof String) - assertEquals("" + smallIntValue, x, "Test for output parameter fails.\n"); + assertEquals("" + smallIntValue, x, TestResource.getResource("R_outputParamFailed")); else - assertEquals(smallIntValue, x, "Test for output parameter fails.\n"); + assertEquals(smallIntValue, x, TestResource.getResource("R_outputParamFailed")); } Class[] int_coercions = {Object.class, Short.class, Integer.class, Long.class, Float.class, Double.class, BigDecimal.class, String.class}; @@ -1716,9 +1716,9 @@ else if (value.toString().equals("0") || value.equals(false) || value.toString() Object x = createValue(int_coercions[i], 3); if (x != null) { if (x instanceof String) - assertEquals("" + IntValue, x, "Test for output parameter fails.\n"); + assertEquals("" + IntValue, x, TestResource.getResource("R_outputParamFailed")); else - assertEquals(IntValue, x, "Test for output parameter fails.\n"); + assertEquals(IntValue, x, TestResource.getResource("R_outputParamFailed")); } } @@ -1729,9 +1729,9 @@ else if (value.toString().equals("0") || value.equals(false) || value.toString() Object x = createValue(bigint_coercions[i], 4); if (x != null) { if (x instanceof String) - assertEquals("" + bigIntValue, x, "Test for output parameter fails.\n"); + assertEquals("" + bigIntValue, x, TestResource.getResource("R_outputParamFailed")); else - assertEquals(bigIntValue, x, "Test for output parameter fails.\n"); + assertEquals(bigIntValue, x, TestResource.getResource("R_outputParamFailed")); } } @@ -1742,9 +1742,9 @@ else if (value.toString().equals("0") || value.equals(false) || value.toString() Object x = createValue(float_coercions[i], 5); if (x != null) { if (x instanceof String) - assertEquals("" + floatDefaultValue, x, "Test for output parameter fails.\n"); + assertEquals("" + floatDefaultValue, x, TestResource.getResource("R_outputParamFailed")); else - assertEquals(floatDefaultValue, x, "Test for output parameter fails.\n"); + assertEquals(floatDefaultValue, x, TestResource.getResource("R_outputParamFailed")); } } @@ -1753,9 +1753,9 @@ else if (value.toString().equals("0") || value.equals(false) || value.toString() Object x = createValue(float_coercions[i], 6); if (x != null) { if (x instanceof String) - assertEquals("" + floatValue, x, "Test for output parameter fails.\n"); + assertEquals("" + floatValue, x, TestResource.getResource("R_outputParamFailed")); else - assertEquals(floatValue, x, "Test for output parameter fails.\n"); + assertEquals(floatValue, x, TestResource.getResource("R_outputParamFailed")); } } @@ -1767,9 +1767,9 @@ else if (value.toString().equals("0") || value.equals(false) || value.toString() Object x = createValue(real_coercions[i], 7); if (x != null) { if (x instanceof String) - assertEquals("" + realValue, x, "Test for output parameter fails for Coercion: " + real_coercions[i] + " for real value.\n"); + assertEquals("" + realValue, x, TestResource.getResource("R_outputParamFailed")); else - assertEquals(realValue, x, "Test for output parameter fails for Coercion: " + real_coercions[i] + " for real value.\n"); + assertEquals(realValue, x, TestResource.getResource("R_outputParamFailed")); } } @@ -1780,9 +1780,9 @@ else if (value.toString().equals("0") || value.equals(false) || value.toString() Object x = createValue(decimalDefault_coercions[i], 8); if (x != null) { if (x instanceof String) - assertEquals("" + decimalDefaultValue, x, "Test for output parameter fails.\n"); + assertEquals("" + decimalDefaultValue, x, TestResource.getResource("R_outputParamFailed")); else - assertEquals(decimalDefaultValue, x, "Test for output parameter fails.\n"); + assertEquals(decimalDefaultValue, x, TestResource.getResource("R_outputParamFailed")); } } @@ -1791,9 +1791,9 @@ else if (value.toString().equals("0") || value.equals(false) || value.toString() Object x = createValue(decimalDefault_coercions[i], 9); if (x != null) { if (x instanceof String) - assertEquals("" + decimalValue, x, "Test for output parameter fails.\n"); + assertEquals("" + decimalValue, x, TestResource.getResource("R_outputParamFailed")); else - assertEquals(decimalValue, x, "Test for output parameter fails.\n"); + assertEquals(decimalValue, x, TestResource.getResource("R_outputParamFailed")); } } @@ -1802,9 +1802,9 @@ else if (value.toString().equals("0") || value.equals(false) || value.toString() Object x = createValue(decimalDefault_coercions[i], 10); if (x != null) { if (x instanceof String) - assertEquals("" + numericDefaultValue, x, "Test for output parameter fails.\n"); + assertEquals("" + numericDefaultValue, x, TestResource.getResource("R_outputParamFailed")); else - assertEquals(numericDefaultValue, x, "Test for output parameter fails.\n"); + assertEquals(numericDefaultValue, x, TestResource.getResource("R_outputParamFailed")); } } @@ -1813,9 +1813,9 @@ else if (value.toString().equals("0") || value.equals(false) || value.toString() Object x = createValue(decimalDefault_coercions[i], 11); if (x != null) { if (x instanceof String) - assertEquals("" + numericValue, x, "Test for output parameter fails.\n"); + assertEquals("" + numericValue, x, TestResource.getResource("R_outputParamFailed")); else - assertEquals(numericValue, x, "Test for output parameter fails.\n"); + assertEquals(numericValue, x, TestResource.getResource("R_outputParamFailed")); } } @@ -1824,9 +1824,9 @@ else if (value.toString().equals("0") || value.equals(false) || value.toString() Object x = createValue(decimalDefault_coercions[i], 12); if (x != null) { if (x instanceof String) - assertEquals("" + smallMoneyValue, x, "Test for output parameter fails.\n"); + assertEquals("" + smallMoneyValue, x, TestResource.getResource("R_outputParamFailed")); else - assertEquals(smallMoneyValue, x, "Test for output parameter fails.\n"); + assertEquals(smallMoneyValue, x, TestResource.getResource("R_outputParamFailed")); } } @@ -1835,9 +1835,9 @@ else if (value.toString().equals("0") || value.equals(false) || value.toString() Object x = createValue(decimalDefault_coercions[i], 13); if (x != null) { if (x instanceof String) - assertEquals("" + moneyValue, x, "Test for output parameter fails.\n"); + assertEquals("" + moneyValue, x, TestResource.getResource("R_outputParamFailed")); else - assertEquals(moneyValue, x, "Test for output parameter fails.\n"); + assertEquals(moneyValue, x, TestResource.getResource("R_outputParamFailed")); } } for (int i = 0; i < decimalDefault_coercions.length; i++) { @@ -1845,9 +1845,9 @@ else if (value.toString().equals("0") || value.equals(false) || value.toString() Object x = createValue(decimalDefault_coercions[i], 14); if (x != null) { if (x instanceof String) - assertEquals("" + decimalValue2, x, "Test for output parameter fails.\n"); + assertEquals("" + decimalValue2, x, TestResource.getResource("R_outputParamFailed")); else - assertEquals(decimalValue2, x, "Test for output parameter fails.\n"); + assertEquals(decimalValue2, x, TestResource.getResource("R_outputParamFailed")); } } @@ -1856,9 +1856,9 @@ else if (value.toString().equals("0") || value.equals(false) || value.toString() Object x = createValue(decimalDefault_coercions[i], 15); if (x != null) { if (x instanceof String) - assertEquals("" + numericValue1, x, "Test for output parameter fails.\n"); + assertEquals("" + numericValue1, x, TestResource.getResource("R_outputParamFailed")); else - assertEquals(numericValue1, x, "Test for output parameter fails.\n"); + assertEquals(numericValue1, x, TestResource.getResource("R_outputParamFailed")); } } @@ -1977,31 +1977,31 @@ private void testOutputProcedureBinaryInorder(String sql) throws SQLException { byte[] received1 = callableStatement.getBytes(1); for (int i = 0; i < expected.length; i++) { - assertEquals(received1[i], expected[i], "Test for output parameter fails.\n"); + assertEquals(received1[i], expected[i], TestResource.getResource("R_outputParamFailed")); } expected = byteValues.get(1); byte[] received2 = callableStatement.getBytes(2); for (int i = 0; i < expected.length; i++) { - assertEquals(received2[i], expected[i], "Test for output parameter fails.\n"); + assertEquals(received2[i], expected[i], TestResource.getResource("R_outputParamFailed")); } expected = byteValues.get(2); byte[] received3 = callableStatement.getBytes(3); for (int i = 0; i < expected.length; i++) { - assertEquals(received3[i], expected[i], "Test for output parameter fails.\n"); + assertEquals(received3[i], expected[i], TestResource.getResource("R_outputParamFailed")); } expected = byteValues.get(3); byte[] received4 = callableStatement.getBytes(4); for (int i = 0; i < expected.length; i++) { - assertEquals(received4[i], expected[i], "Test for output parameter fails.\n"); + assertEquals(received4[i], expected[i], TestResource.getResource("R_outputParamFailed")); } expected = byteValues.get(4); byte[] received5 = callableStatement.getBytes(5); for (int i = 0; i < expected.length; i++) { - assertEquals(received5[i], expected[i], "Test for output parameter fails.\n"); + assertEquals(received5[i], expected[i], TestResource.getResource("R_outputParamFailed")); } } catch (Exception e) { @@ -2274,20 +2274,20 @@ private void testOutputProcedureDateInorder(String sql) throws SQLException { callableStatement.registerOutParameter(18, microsoft.sql.Types.DATETIMEOFFSET, 2); callableStatement.execute(); - assertEquals(callableStatement.getDate(1), callableStatement.getDate(2), "Test for output parameter fails.\n"); + assertEquals(callableStatement.getDate(1), callableStatement.getDate(2), TestResource.getResource("R_outputParamFailed")); - assertEquals(callableStatement.getTimestamp(3), callableStatement.getTimestamp(4), "Test for output parameter fails.\n"); + assertEquals(callableStatement.getTimestamp(3), callableStatement.getTimestamp(4), TestResource.getResource("R_outputParamFailed")); - assertEquals(callableStatement.getDateTimeOffset(5), callableStatement.getDateTimeOffset(6), "Test for output parameter fails.\n"); + assertEquals(callableStatement.getDateTimeOffset(5), callableStatement.getDateTimeOffset(6), TestResource.getResource("R_outputParamFailed")); - assertEquals(callableStatement.getTime(7), callableStatement.getTime(8), "Test for output parameter fails.\n"); + assertEquals(callableStatement.getTime(7), callableStatement.getTime(8), TestResource.getResource("R_outputParamFailed")); assertEquals(callableStatement.getDateTime(9), // actual plain callableStatement.getDateTime(10), // received expected enc - "Test for output parameter fails.\n"); - assertEquals(callableStatement.getSmallDateTime(11), callableStatement.getSmallDateTime(12), "Test for output parameter fails.\n"); - assertEquals(callableStatement.getTimestamp(13), callableStatement.getTimestamp(14), "Test for output parameter fails.\n"); - assertEquals(callableStatement.getTime(15).getTime(), callableStatement.getTime(16).getTime(), "Test for output parameter fails.\n"); - assertEquals(callableStatement.getDateTimeOffset(17), callableStatement.getDateTimeOffset(18), "Test for output parameter fails.\n"); + TestResource.getResource("R_outputParamFailed")); + assertEquals(callableStatement.getSmallDateTime(11), callableStatement.getSmallDateTime(12), TestResource.getResource("R_outputParamFailed")); + assertEquals(callableStatement.getTimestamp(13), callableStatement.getTimestamp(14), TestResource.getResource("R_outputParamFailed")); + assertEquals(callableStatement.getTime(15).getTime(), callableStatement.getTime(16).getTime(), TestResource.getResource("R_outputParamFailed")); + assertEquals(callableStatement.getDateTimeOffset(17), callableStatement.getDateTimeOffset(18), TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { @@ -2318,20 +2318,20 @@ private void testOutputProcedureDateInorderObject(String sql) throws SQLExceptio callableStatement.registerOutParameter(18, microsoft.sql.Types.DATETIMEOFFSET, 2); callableStatement.execute(); - assertEquals(callableStatement.getObject(1), callableStatement.getObject(2), "Test for output parameter fails.\n"); + assertEquals(callableStatement.getObject(1), callableStatement.getObject(2), TestResource.getResource("R_outputParamFailed")); - assertEquals(callableStatement.getObject(3), callableStatement.getObject(4), "Test for output parameter fails.\n"); + assertEquals(callableStatement.getObject(3), callableStatement.getObject(4), TestResource.getResource("R_outputParamFailed")); - assertEquals(callableStatement.getObject(5), callableStatement.getObject(6), "Test for output parameter fails.\n"); + assertEquals(callableStatement.getObject(5), callableStatement.getObject(6), TestResource.getResource("R_outputParamFailed")); - assertEquals(callableStatement.getObject(7), callableStatement.getObject(8), "Test for output parameter fails.\n"); + assertEquals(callableStatement.getObject(7), callableStatement.getObject(8), TestResource.getResource("R_outputParamFailed")); assertEquals(callableStatement.getObject(9), // actual plain callableStatement.getObject(10), // received expected enc - "Test for output parameter fails.\n"); - assertEquals(callableStatement.getObject(11), callableStatement.getObject(12), "Test for output parameter fails.\n"); - assertEquals(callableStatement.getObject(13), callableStatement.getObject(14), "Test for output parameter fails.\n"); - assertEquals(callableStatement.getObject(15), callableStatement.getObject(16), "Test for output parameter fails.\n"); - assertEquals(callableStatement.getObject(17), callableStatement.getObject(18), "Test for output parameter fails.\n"); + TestResource.getResource("R_outputParamFailed")); + assertEquals(callableStatement.getObject(11), callableStatement.getObject(12), TestResource.getResource("R_outputParamFailed")); + assertEquals(callableStatement.getObject(13), callableStatement.getObject(14), TestResource.getResource("R_outputParamFailed")); + assertEquals(callableStatement.getObject(15), callableStatement.getObject(16), TestResource.getResource("R_outputParamFailed")); + assertEquals(callableStatement.getObject(17), callableStatement.getObject(18), TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { @@ -2364,16 +2364,16 @@ private void testOutputProcedureBatchInorder(String sql) throws SQLException { callableStatement.execute(); int intValue2 = callableStatement.getInt(1); - assertEquals("" + intValue2, numericValues[3], "Test for output parameter fails.\n"); + assertEquals("" + intValue2, numericValues[3], TestResource.getResource("R_outputParamFailed")); double floatValue0 = callableStatement.getDouble(2); - assertEquals("" + floatValue0, numericValues[5], "Test for output parameter fails.\n"); + assertEquals("" + floatValue0, numericValues[5], TestResource.getResource("R_outputParamFailed")); short shortValue3 = callableStatement.getShort(3); - assertEquals("" + shortValue3, numericValues[2], "Test for output parameter fails.\n"); + assertEquals("" + shortValue3, numericValues[2], TestResource.getResource("R_outputParamFailed")); BigDecimal smallmoneyValue = callableStatement.getSmallMoney(4); - assertEquals("" + smallmoneyValue, numericValues[12], "Test for output parameter fails.\n"); + assertEquals("" + smallmoneyValue, numericValues[12], TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { fail(e.toString()); @@ -2422,11 +2422,11 @@ private void testMixedProcedureDateScaleInorder(String sql) throws SQLException callableStatement.setDateTimeOffset(5, (DateTimeOffset) dateValues.get(6), 2); callableStatement.execute(); - assertEquals(callableStatement.getTimestamp(1), callableStatement.getTimestamp(2), "Test for output parameter fails.\n"); + assertEquals(callableStatement.getTimestamp(1), callableStatement.getTimestamp(2), TestResource.getResource("R_outputParamFailed")); - assertEquals(callableStatement.getTime(3), callableStatement.getTime(4), "Test for output parameter fails.\n"); + assertEquals(callableStatement.getTime(3), callableStatement.getTime(4), TestResource.getResource("R_outputParamFailed")); - assertEquals(callableStatement.getDateTimeOffset(5), callableStatement.getDateTimeOffset(6), "Test for output parameter fails.\n"); + assertEquals(callableStatement.getDateTimeOffset(5), callableStatement.getDateTimeOffset(6), TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { @@ -2448,11 +2448,11 @@ private void testMixedProcedureDateScaleWithParameterName(String sql) throws SQL callableStatement.setDateTimeOffset("p5", (DateTimeOffset) dateValues.get(6), 2); callableStatement.execute(); - assertEquals(callableStatement.getTimestamp(1), callableStatement.getTimestamp(2), "Test for output parameter fails.\n"); + assertEquals(callableStatement.getTimestamp(1), callableStatement.getTimestamp(2), TestResource.getResource("R_outputParamFailed")); - assertEquals(callableStatement.getTime(3), callableStatement.getTime(4), "Test for output parameter fails.\n"); + assertEquals(callableStatement.getTime(3), callableStatement.getTime(4), TestResource.getResource("R_outputParamFailed")); - assertEquals(callableStatement.getDateTimeOffset(5), callableStatement.getDateTimeOffset(6), "Test for output parameter fails.\n"); + assertEquals(callableStatement.getDateTimeOffset(5), callableStatement.getDateTimeOffset(6), TestResource.getResource("R_outputParamFailed")); } catch (Exception e) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/JDBCEncryptionDecryptionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/JDBCEncryptionDecryptionTest.java index 06e44276b..36ec08da9 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/JDBCEncryptionDecryptionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/JDBCEncryptionDecryptionTest.java @@ -23,6 +23,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; import com.microsoft.sqlserver.jdbc.SQLServerResultSet; import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.util.RandomData; import com.microsoft.sqlserver.testframework.util.Util; @@ -568,12 +569,12 @@ private void testGetObject(ResultSet rs, && objectValue3.equalsIgnoreCase("" + values[index]); if (("" + values[index]).length() >= 1000) { - assertTrue(matches, "\nDecryption failed with getObject() at index: " + i + ", " + (i + 1) + ", " + (i + 2) - + ".\nExpected Value at index: " + index); + assertTrue(matches, TestResource.getResource("R_decryptionFailed") + "getObject(): " + i + ", " + (i + 1) + ", " + (i + 2) + + ".\n" + TestResource.getResource("R_expectedValueAtIndex") + index); } else { - assertTrue(matches, "\nDecryption failed with getObject(): " + objectValue1 + ", " + objectValue2 + ", " + objectValue3 - + ".\nExpected Value: " + values[index]); + assertTrue(matches, TestResource.getResource("R_decryptionFailed") + "getObject(): " + objectValue1 + ", " + objectValue2 + ", " + objectValue3 + + ".\n" + TestResource.getResource("R_expectedValue") + values[index]); } } finally { @@ -605,8 +606,7 @@ else if (rs.getMetaData().getColumnTypeName(i).equalsIgnoreCase("datetime")) { assertTrue( objectValue1.equalsIgnoreCase("" + expected) && objectValue2.equalsIgnoreCase("" + expected) && objectValue3.equalsIgnoreCase("" + expected), - "\nDecryption failed with getObject(): " + objectValue1 + ", " + objectValue2 + ", " + objectValue3 + ".\nExpected Value: " - + expected); + TestResource.getResource("R_decryptionFailed") + "getObject(): " + objectValue1 + ", " + objectValue2 + ", " + objectValue3 + ".\n" + TestResource.getResource("R_expectedValue") + expected); } finally { index++; @@ -633,7 +633,7 @@ private void testGetObjectForBinary(ResultSet rs, if (null != values.get(index)) { for (int j = 0; j < expectedBytes.length; j++) { assertTrue(expectedBytes[j] == objectValue1[j] && expectedBytes[j] == objectValue2[j] && expectedBytes[j] == objectValue3[j], - "Decryption failed with getObject(): " + objectValue1 + ", " + objectValue2 + ", " + objectValue3 + ".\n"); + TestResource.getResource("R_decryptionFailed") + "getObject(): " + objectValue1 + ", " + objectValue2 + ", " + objectValue3 + ".\n"); } } } @@ -685,8 +685,8 @@ else if (values[index].equalsIgnoreCase("-3.4E38")) { assertTrue( decimalValue1.equalsIgnoreCase("" + values[index]) && decimalValue2.equalsIgnoreCase("" + values[index]) && decimalValue3.equalsIgnoreCase("" + values[index]), - "\nDecryption failed with getBigDecimal(): " + decimalValue1 + ", " + decimalValue2 + ", " + decimalValue3 - + ".\nExpected Value: " + values[index]); + TestResource.getResource("R_decryptionFailed") + "getBigDecimal(): " + decimalValue1 + ", " + decimalValue2 + ", " + decimalValue3 + + ".\n" + TestResource.getResource("R_expectedValue") + values[index]); } finally { index++; @@ -720,13 +720,12 @@ else if (stringValue1.equalsIgnoreCase("1") && (values[index].equalsIgnoreCase(" && stringValue3.equalsIgnoreCase("" + values[index]); if (("" + values[index]).length() >= 1000) { - assertTrue(matches, "\nDecryption failed with getString() at index: " + i + ", " + (i + 1) + ", " + (i + 2) - + ".\nExpected Value at index: " + index); - + assertTrue(matches, TestResource.getResource("R_decryptionFailed") + "getString():" + i + ", " + (i + 1) + ", " + (i + 2) + + ".\n" + TestResource.getResource("R_expectedValue") + index); } else { - assertTrue(matches, "\nDecryption failed with getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 - + ".\nExpected Value: " + values[index]); + assertTrue(matches, TestResource.getResource("R_decryptionFailed") + "getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + + ".\n" + TestResource.getResource("R_expectedValue") + values[index]); } } finally { @@ -752,8 +751,8 @@ private void testGetStringForDate(ResultSet rs, assertTrue( stringValue1.contains("" + values.get(index)) && stringValue2.contains("" + values.get(index)) && stringValue3.contains("" + values.get(index)), - "\nDecryption failed with getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 - + ".\nExpected Value: " + values.get(index)); + TestResource.getResource("R_decryptionFailed") + "getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + + ".\n" + TestResource.getResource("R_expectedValue") + values.get(index)); } else if (index == 4) // round value for datetime { @@ -761,8 +760,8 @@ else if (index == 4) // round value for datetime assertTrue( stringValue1.equalsIgnoreCase("" + datetimeValue) && stringValue2.equalsIgnoreCase("" + datetimeValue) && stringValue3.equalsIgnoreCase("" + datetimeValue), - "\nDecryption failed with getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 - + ".\nExpected Value: " + datetimeValue); + TestResource.getResource("R_decryptionFailed") + "getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + + ".\n" + TestResource.getResource("R_expectedValue") + datetimeValue); } else if (index == 5) // round value for smalldatetime { @@ -770,15 +769,15 @@ else if (index == 5) // round value for smalldatetime assertTrue( stringValue1.equalsIgnoreCase("" + smalldatetimeValue) && stringValue2.equalsIgnoreCase("" + smalldatetimeValue) && stringValue3.equalsIgnoreCase("" + smalldatetimeValue), - "\nDecryption failed with getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 - + ".\nExpected Value: " + smalldatetimeValue); + TestResource.getResource("R_decryptionFailed") + "getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + + ".\n" + TestResource.getResource("R_expectedValue") + smalldatetimeValue); } else { assertTrue( stringValue1.contains("" + values.get(index)) && stringValue2.contains("" + values.get(index)) && stringValue3.contains("" + values.get(index)), - "\nDecryption failed with getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 - + ".\nExpected Value: " + values.get(index)); + TestResource.getResource("R_decryptionFailed") + "getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + + ".\n" + TestResource.getResource("R_expectedValue") + values.get(index)); } } finally { @@ -806,7 +805,7 @@ private void testGetBytes(ResultSet rs, if (null != values.get(index)) { for (int j = 0; j < expectedBytes.length; j++) { assertTrue(expectedBytes[j] == b1[j] && expectedBytes[j] == b2[j] && expectedBytes[j] == b3[j], - "Decryption failed with getObject(): " + b1 + ", " + b2 + ", " + b3 + ".\n"); + TestResource.getResource("R_decryptionFailed") + "getObject(): " + b1 + ", " + b2 + ", " + b3 + ".\n"); } } } @@ -841,7 +840,7 @@ private void testGetStringForBinary(ResultSet rs, try { assertTrue(stringValue1.startsWith(expectedStr) && stringValue2.startsWith(expectedStr) && stringValue3.startsWith(expectedStr), - "\nDecryption failed with getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + ".\nExpected Value: " + TestResource.getResource("R_decryptionFailed") + "getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + ".\n" + TestResource.getResource("R_expectedValue") + expectedStr); } finally { @@ -907,17 +906,17 @@ private void testGetDate(ResultSet rs, break; default: - fail("Switch case is not matched with data"); + fail(TestResource.getResource("R_switchFailed")); } assertTrue( stringValue1.equalsIgnoreCase(expected) && stringValue2.equalsIgnoreCase(expected) && stringValue3.equalsIgnoreCase(expected), - "\nDecryption failed with testGetDate: " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + ".\nExpected Value: " + TestResource.getResource("R_decryptionFailed") + "testGetDate: " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + ".\n" + TestResource.getResource("R_expectedValue") + expected); } else { - fail("Result set is not instance of SQLServerResultSet"); + fail(TestResource.getResource("R_resultsetNotInstance")); } } } @@ -1120,7 +1119,7 @@ else if (expectedValue.equalsIgnoreCase("-3.4E+38")) { assertTrue( value1.equalsIgnoreCase("" + expectedValue) && value2.equalsIgnoreCase("" + expectedValue) && value3.equalsIgnoreCase("" + expectedValue), - "\nDecryption failed with getBigDecimal(): " + value1 + ", " + value2 + ", " + value3 + ".\nExpected Value: " + expectedValue); + TestResource.getResource("R_decryptionFailed") + "getBigDecimal(): " + value1 + ", " + value2+ ", " + value3 + ".\n" + TestResource.getResource("R_expectedValue")); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/PrecisionScaleTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/PrecisionScaleTest.java index 4d7b8b3d0..0a28036f9 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/PrecisionScaleTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/PrecisionScaleTest.java @@ -26,6 +26,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; import com.microsoft.sqlserver.jdbc.SQLServerResultSet; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.util.Util; /** @@ -201,15 +202,15 @@ private void testGetString(ResultSet rs, try { if (rs.getMetaData().getColumnTypeName(i).equalsIgnoreCase("time")) { assertTrue(stringValue2.equalsIgnoreCase("" + values[index]) && stringValue3.equalsIgnoreCase("" + values[index]), - "\nDecryption failed with getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 - + ".\nExpected Value: " + values[index]); + TestResource.getResource("R_decryptionFailed") + "getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + + ".\n" + TestResource.getResource("R_expectedValue") + ": " + values[index]); } else { assertTrue( values[index].contains(stringValue1) && stringValue2.equalsIgnoreCase("" + values[index]) && stringValue3.equalsIgnoreCase("" + values[index]), - "\nDecryption failed with getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 - + ".\nExpected Value: " + values[index]); + TestResource.getResource("R_decryptionFailed") + "getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + + TestResource.getResource("R_expectedValue") + values[index]); } } finally { @@ -232,8 +233,8 @@ private void testGetBigDecimal(ResultSet rs, assertTrue( decimalValue1.equalsIgnoreCase(values[index]) && decimalValue2.equalsIgnoreCase(values[index]) && decimalValue3.equalsIgnoreCase(values[index]), - "Decryption failed with getBigDecimal(): " + decimalValue1 + ", " + decimalValue2 + ", " + decimalValue3 - + "\nExpected value: " + values[index]); + TestResource.getResource("R_decryptionFailed") + "getBigDecimal(): " + decimalValue1 + ", " + decimalValue2 + ", " + decimalValue3 + + "\n" + TestResource.getResource("R_expectedValue") + ": " + values[index]); } finally { @@ -260,7 +261,7 @@ private void testGetObject(ResultSet rs, assertTrue( objectValue1.equalsIgnoreCase(values[index]) && objectValue2.equalsIgnoreCase(values[index]) && objectValue3.equalsIgnoreCase(values[index]), - "Decryption failed with getObject(): " + objectValue1 + ", " + objectValue2 + ", " + objectValue3 + "\nExpected value: " + TestResource.getResource("R_decryptionFailed") + "getObject(): " + objectValue1 + ", " + objectValue2 + ", " + objectValue3 + "\n" + TestResource.getResource("R_expectedValue") + ": " + values[index]); } @@ -321,14 +322,13 @@ private void testGetDate(ResultSet rs, break; default: - fail("Switch case is not matched with data"); } try { assertTrue( stringValue1.equalsIgnoreCase(dates[index]) && stringValue2.equalsIgnoreCase(dates[index]) && stringValue3.equalsIgnoreCase(dates[index]), - "Decryption failed with getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + "\nExpected value: " + TestResource.getResource("R_decryptionFailed") + "getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + "\n" + TestResource.getResource("R_expectedValue") + ": " + dates[index]); } finally { @@ -337,7 +337,7 @@ private void testGetDate(ResultSet rs, } else { - throw new Exception("Result set is not instance of SQLServerResultSet"); + throw new Exception(TestResource.getResource("R_resultsetNotInstance")); } } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java b/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java index 6873bbffe..fda54026a 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java @@ -32,6 +32,8 @@ import static org.junit.Assert.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; +import com.microsoft.sqlserver.jdbc.TestResource;; + /** * Tests JDBC 4.3 APIs * @@ -56,14 +58,14 @@ public void connectionBuilderTest() throws TestAbortedException, SQLException { superShardingKey = ds.createShardingKeyBuilder().subkey("EASTERN_REGION", JDBCType.VARCHAR).build(); } catch (SQLException e) { - assert (e.getMessage().contains("not implemented")); + assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); } try { shardingKey = ds.createShardingKeyBuilder().subkey("PITTSBURGH_BRANCH", JDBCType.VARCHAR).build(); } catch (SQLException e) { - assert (e.getMessage().contains("not implemented")); + assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); } try { @@ -71,7 +73,7 @@ public void connectionBuilderTest() throws TestAbortedException, SQLException { .build(); } catch (SQLException e) { - assert (e.getMessage().contains("not implemented")); + assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); } } @@ -90,14 +92,14 @@ public void xaConnectionBuilderTest() throws TestAbortedException, SQLException superShardingKey = ds.createShardingKeyBuilder().subkey("EASTERN_REGION", JDBCType.VARCHAR).build(); } catch (SQLException e) { - assert (e.getMessage().contains("not implemented")); + assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); } try { shardingKey = ds.createShardingKeyBuilder().subkey("PITTSBURGH_BRANCH", JDBCType.VARCHAR).build(); } catch (SQLException e) { - assert (e.getMessage().contains("not implemented")); + assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); } try { @@ -105,7 +107,7 @@ public void xaConnectionBuilderTest() throws TestAbortedException, SQLException .superShardingKey(superShardingKey).build(); } catch (SQLException e) { - assert (e.getMessage().contains("not implemented")); + assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); } } @@ -123,21 +125,21 @@ public void connectionPoolDataSourceTest() throws TestAbortedException, SQLExcep superShardingKey = ds.createShardingKeyBuilder().subkey("EASTERN_REGION", JDBCType.VARCHAR).build(); } catch (SQLException e) { - assert (e.getMessage().contains("not implemented")); + assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); } try { shardingKey = ds.createShardingKeyBuilder().subkey("PITTSBURGH_BRANCH", JDBCType.VARCHAR).build(); } catch (SQLException e) { - assert (e.getMessage().contains("not implemented")); + assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); } try { PooledConnection con = ds.createPooledConnectionBuilder().user("rafa").password("tennis").shardingKey(shardingKey) .superShardingKey(superShardingKey).build(); } catch (SQLException e) { - assert (e.getMessage().contains("not implemented")); + assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); } } @@ -155,13 +157,13 @@ public void setShardingKeyIfValidTest() throws TestAbortedException, SQLExceptio connection43.setShardingKeyIfValid(shardingKey, 10); } catch (SQLException e) { - assert (e.getMessage().contains("not implemented")); + assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); } try { connection43.setShardingKeyIfValid(shardingKey, superShardingKey, 10); } catch (SQLException e) { - assert (e.getMessage().contains("not implemented")); + assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); } } @@ -180,13 +182,13 @@ public void setShardingKeyTest() throws TestAbortedException, SQLException { connection43.setShardingKey(shardingKey); } catch (SQLException e) { - assert (e.getMessage().contains("not implemented")); + assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); } try { connection43.setShardingKey(shardingKey, superShardingKey); } catch (SQLException e) { - assert (e.getMessage().contains("not implemented")); + assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); } } @@ -225,4 +227,4 @@ public void deregisterDriverTest() throws SQLException, ClassNotFoundException { DriverManager.registerDriver(current); } -} \ No newline at end of file +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java new file mode 100644 index 000000000..4d6e59dc4 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java @@ -0,0 +1,172 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + +package com.microsoft.sqlserver.jdbc; + +import java.util.ListResourceBundle; + +/** + * A simple resource bundle containing the strings for localizing. + * + */ +public final class TestResource extends ListResourceBundle { + public static String getResource(String key) { + return TestResource.getBundle("com.microsoft.sqlserver.jdbc.TestResource").getString(key); + } + + protected Object[][] getContents() { + return contents; + } + + // the keys must be prefixed with R_ to denote they are resource strings and their names should follow the camelCasing + // convention and be descriptive + static final Object[][] contents = { + {"R_wrongEnv", "Aborting test: As this is not the right environement: "}, + {"R_fipsPropertyNotSet", "Aborting test case as FIPS_ENV property is not set."}, + {"R_invalidTrustCert", "Invalid TrustServerCertificate value."}, + {"R_invalidEncrypt", "Invalid encrypt value."}, + {"R_notImplemented", "not implemented"}, + {"R_resultsetClosed", "The result set is closed."}, + {"R_resultsetNull", "The result set is null."}, + {"R_invalidFipsConfig", "Unable to verify FIPS mode settings."}, + {"R_shouldBeEnabled", "should be enabled."}, + {"R_StoredProcedureNotFound", "Could not find stored procedure"}, + {"R_givenValueType", "The given value of type"}, + {"R_lengthTruncated", " The inserted length is truncated or not correct!"}, + {"R_timeValueTruncated", " The time value is truncated or not correct!"}, + {"R_invalidErrorMessage", "Invalid Error Message: "}, + {"R_expectedFailPassed", "Expected failure did not fail"}, + {"R_dataTypeNotFound", "Cannot find data type"}, + {"R_illegalCharWkt", "Illegal character at wkt position "}, + {"R_errorMessage", " Error message: "}, + {"R_createDropViewFailed", "Create/drop view with preparedStatement failed!"}, + {"R_createDropSchemaFailed", "Create/drop schema with preparedStatement failed!"}, + {"R_createDropTableFailed", "Create/drop table with preparedStatement failed!"}, + {"R_createDropAlterTableFailed", "Create/drop/alter table with preparedStatement failed!"}, + {"R_grantFailed", "grant table with preparedStatement failed!"}, + {"R_connectionIsClosed", "The connection is closed."}, + {"R_connectionIsNotClosed", "The connection is not closed."}, + {"R_invalidExceptionMessage", "Invalid exception message"}, + {"R_failedValidate", "failed to validate values in $0} "}, + {"R_tableNotDropped", "table not dropped. "}, + {"R_connectionReset", "Connection reset"}, + {"R_unknownException", "Unknown exception"}, + {"R_deadConnection", "Dead connection should be invalid"}, + {"R_wrongExceptionMessage", "Wrong exception message"}, + {"R_parameterNotDefined", "Parameter {0} was not defined"}, + {"R_unexpectedExceptionContent", "Unexpected content in exception message"}, + {"R_conversionFailed", "Conversion failed when converting {0} to {1} data type"}, + {"R_invalidQueryTimeout", "The query timeout value {0} is not valid."}, + {"R_skipAzure", "Skipping test case on Azure SQL."}, + {"R_expectedExceptionNotThrown", "Expected exception is not thrown."}, + {"R_errorNotCalled", "Error occurred is not called."}, + {"R_errorCalled", "Error occurred is called."}, + {"R_supportUnwrapping", "{0} supports unwrapping."}, + {"R_cantAccessSnapshot", "Cant access the TRANSACTION_SNAPSHOT "}, + {"R_newConnectionShouldBeValid", "Newly created connection should be valid"}, + {"R_closedConnectionShouldBeInvalid", "Closed connection should be invalid"}, + {"R_noExceptionNegativeTimeout", "No exception thrown with negative timeout"}, + {"R_noExceptionClosedConnection", "No exception thrown calling getClientConnectionId on a closed connection"}, + {"R_clientConnectionIdNull", "ClientConnectionId is null"}, + {"R_valuesAreDifferent", "Values are different"}, + {"R_parrentLoggerNameWrong", "Parent Logger name is wrong"}, + {"R_unexpectedWrongDB", "Unexpected: ClientConnectionId is not in exception message due to wrong DB"}, + {"R_unexpectedWrongHost", "Unexpected: ClientConnectionId is in exception message due to wrong host"}, + {"R_cannotOpenDatabase", "Cannot open database"}, + {"R_shouldNotConnect", "Should not have connected"}, + {"R_loginFailed", "Login failed"}, + {"R_exitedMoreSeconds", "Exited in more than {0} seconds."}, + {"R_exitedLessSeconds", "Exited in less than {0} seconds."}, + {"R_invalidArgumentExecutor", "The argument executor is not valid"}, + {"R_threadInterruptNotSet", "Thread's interrupt status is not set."}, + {"R_connectMirrored", "Connecting to a mirrored"}, + {"R_trustStorePasswordNotSet", "The DataSource trustStore password needs to be set."}, + {"R_invalidObjectName", "Invalid object name"}, + {"R_tempTAbleNotRemoved", "Temporary table is not removed."}, + {"R_firstConnectionNotClosed", "First connection is not closed"}, + {"R_connectionNotClosedWithPoolClose", "Connection is not closed with pool close"}, + {"R_connectionNotClosedWithPoolClose", "Unexpected: ClientConnectionId is null from Pool"}, + {"R_idFromPoolNotSame", "ClientConnection Ids from pool are not the same."}, + {"R_noProtocolVersion", "protocol version is not enabled or not supported by the client."}, + {"R_protocolException", "Any protocol other than TLSv1, TLSv1.1, and TLSv1.2 should throw Exception"}, + {"R_invalidProtocolLabel", "SSL Protocol {0} label is not valid. Only TLS, TLSv1, TLSv1.1, and TLSv1.2 are supported."}, + {"R_SQLServerResourceMessage", "Message should be from SQL Server resources: "}, + {"R_shouldThrowException", "Exception should have been thrown"}, + {"R_tcpipConnectionToHost", "The TCP/IP connection to the host"}, + {"R_queryTimedOut", "The query has timed out."}, + {"R_readTimedOut", "Read timed out"}, + {"R_unexpectedErrorMessage", "Unexpected error message occured!"}, + {"R_warningsNotFound", "Warnings not found!"}, + {"R_warningsFound", "Warnings found!"}, + {"R_causeShouldNotBeNull", "Cause should not be null."}, + {"R_causeShouldBeInstance", "Cause should be instance of {0}."}, + {"R_connShouldNotBeClosed", "Connection should not be closed"}, + {"R_connShouldNotBeOpen", "Connection should not be open"}, + {"R_incorrectDriverNameFormat", "Driver name is not a correct format! "}, + {"R_incorrectDriverVersionFormat", "Driver version number should be four parts! "}, + {"R_previousShouldThrow", "Previous should have thrown an exception"}, + {"R_objectMissingOrEmpty", "An object or column name is missing or empty."}, + {"R_dbNameIsCurrentDB", "The database name component of the object qualifier must be the name of the current database."}, + {"R_numKeysIncorrect", "number of foreign key entries is incorrect."}, + {"R_manifestNotFound", "Manifest file does not exist on classpath so ignoring test"}, + {"R_buildVersionError", "build version should be greater than driver versions for non SNAPSHOT versions, and same for SNAPSHOT versions"}, + {"R_getURLContainsPwd", "Get URL should not have password attribute / property."}, + {"R_userNameNull", "Username should not be null"}, + {"R_userNameNotMatch", "Username does not match UserName from Connection String."}, + {"R_nameEmpty", "{0} name should not be empty."}, + {"R_nameNull", "{0} name should not be NULL."}, + {"R_atLeastOneFound", "At least one {0} should be found."}, + {"R_noSchemaShouldFail", "As we are not supplying schema it should fail."}, + {"R_addBatchFailed", "addBatch does not add the SQL Statements to Batch, call to addBatch failed"}, + {"R_insertBatchFailed", "affected rows does not match with batch size. Insert failed"}, + {"R_savePointError", "Savepoint {0} should be {1}."}, + {"R_Incompat_SQLServerVersion", "Aborting test case as SQL Server version is not compatible with Always encrypted"}, + {"R_noKeyStore", "Aborting test case as no java key store and alias name exists."}, + {"R_badStreamLength", "The stream value is not the specified length. The specified length was"}, + {"R_streamReadError", "An error occurred while reading the value from the stream object. Error"}, + {"R_SQLStateNull", "SQLState should not be null"}, + {"R_blobFreed", "This Blob oject has been freed."}, + {"R_streamNull", "Stream is null when data is not."}, + {"R_incorrectUpdateCount", "Incorrect updateCount."}, + {"R_testInterleaved", "Test interleaved inserts and warnings"}, + {"R_errorFollowInserts", "Test error followed by inserts"}, + {"R_errorFollow50280", "Test insert followed by non-fatal error (50280)"}, + {"R_syntaxErrorDateConvert", "Syntax error converting date"}, + {"R_dateConvertError", "Conversion failed when converting date"}, + {"R_incompatJDBC", "Aborting test case as JDBC version is not compatible."}, + {"R_unexpectedException", "Unexpected exception occurred"}, + {"R_addBatchFailed", "addBatch failed"}, + {"R_executeBatchFailed", "executeBatch failed"}, + {"R_customErrorMessage", "Custom error message: col1 should be higher than 10"}, + {"R_setDataNotEqual", "Received data not equal to setdata"}, + {"R_syntaxMatchError", "Syntax translation does not match for query"}, + {"R_valueNotMatch", "Value does not match: "}, + {"R_incorrectDefault", "Incorrect default"}, + {"R_shouldBeSupported", "should be supported."}, + {"R_operationNotSupported", "This operation is not supported."}, + {"R_paramNotRecognized", "Not all parameters are recognized by driver."}, + {"R_invalidGetPreparedStatementHandle", "Invalid use of getPreparedStatementHandle() after statement close expected."}, + {"R_cancellationFailed", "Cancellation failed."}, + {"R_executionNotTimeout", "Execution did not timeout."}, + {"R_executionTooLong", "Execution took too long."}, + {"R_executionNotLong", "Execution did not take long enough."}, + {"R_queryCancelled", "The query was canceled."}, + {"R_statementShouldBeClosed", "statement should be closed since resultset is closed."}, + {"R_statementShouldBeOpened", "statement should be opened since resultset is opened."}, + {"R_shouldBeWrapper", "{0} should be a wrapper for {1}."}, + {"R_shouldNotBeWrapper", "{0} should not be a wrapper for {1}."}, + {"R_outputParamFailed", "Test for output parameter failed."}, + {"R_inputParamFailed", "Test for input parameter failed."}, + {"R_decryptionFailed", "Decryption failed"}, + {"R_expectedValue", "Expected value: "}, + {"R_expectedValueAtIndex", "Expected value at index: "}, + {"R_switchFailed", "Switch case is not matched with data"}, + {"R_resultsetNotInstance", "Result set is not instance of SQLServerResultSet"}, + + }; +} 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 2de49b360..49e72a880 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyColumnMappingTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyColumnMappingTest.java @@ -13,6 +13,7 @@ import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.concurrent.ThreadLocalRandom; +import java.text.MessageFormat; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -28,6 +29,8 @@ import com.microsoft.sqlserver.testframework.sqlType.SqlType; import com.microsoft.sqlserver.testframework.util.ComparisonUtil; +import com.microsoft.sqlserver.jdbc.TestResource; + /** * Test BulkCopy Column Mapping */ @@ -193,7 +196,10 @@ void testRepetativeCM() { validateValuesRepetativeCM(con, sourceTable1, destTable); } catch (SQLException e) { - fail("failed to validate values in " + sourceTable1.getTableName() + " and " + destTable.getTableName() + "\n" + e.getMessage()); + MessageFormat form = new MessageFormat(TestResource.getResource("R_failedValidate")); + Object[] msgArgs = {sourceTable1.getTableName()+" and"+destTable.getTableName()}; + + fail(form.format(msgArgs) + "\n" + destTable.getTableName() + "\n" + e.getMessage()); } dropTable(sourceTable1.getEscapedTableName()); dropTable(destTable.getEscapedTableName()); @@ -367,7 +373,7 @@ private void dropTable(String tableName) { stmt.execute(dropSQL); } catch (SQLException e) { - fail("table " + tableName + " not dropped\n" + e.getMessage()); + fail(tableName + " " + TestResource.getResource("R_tableNotDropped") + "\n" + e.getMessage()); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyConnectionTest.java index abcae31a8..bbc3f7100 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyConnectionTest.java @@ -115,10 +115,10 @@ public void execute() throws SQLException { } /** - * BulkCopy:test empty connenction string + * BulkCopy:test empty connection string */ @Test - @DisplayName("BulkCopy:test empty connenction string") + @DisplayName("BulkCopy:test empty connection string") void testInvalidConnection3() { assertThrows(SQLException.class, new org.junit.jupiter.api.function.Executable() { @Override diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyTestUtil.java b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyTestUtil.java index bfdd83cb5..78b6ac18d 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyTestUtil.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyTestUtil.java @@ -16,6 +16,7 @@ import com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord; import com.microsoft.sqlserver.jdbc.SQLServerBulkCopy; import com.microsoft.sqlserver.jdbc.bulkCopy.BulkCopyTestWrapper.ColumnMap; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.DBConnection; import com.microsoft.sqlserver.testframework.DBResultSet; import com.microsoft.sqlserver.testframework.DBStatement; @@ -203,7 +204,7 @@ else if ((!currentMap.sourceIsInt) && (!currentMap.destIsInt)) { } bulkCopy.writeToServer((ResultSet) srcResultSet.product()); if (fail) - fail("bulkCopy.writeToServer did not fail when it should have"); + fail(TestResource.getResource("R_expectedExceptionNotThrown")); if (validateResult) { validateValues(con, sourceTable, destinationTable); } @@ -269,7 +270,7 @@ else if ((!currentMap.sourceIsInt) && (!currentMap.destIsInt)) { } bulkCopy.writeToServer((ResultSet) srcResultSet.product()); if (fail) - fail("bulkCopy.writeToServer did not fail when it should have"); + fail(TestResource.getResource("R_expectedExceptionNotThrown")); bulkCopy.close(); if (validateResult) { validateValues(con, sourceTable, destinationTable); @@ -394,4 +395,4 @@ static void validateValues( } } } -} \ No newline at end of file +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/ISQLServerBulkRecordIssuesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/ISQLServerBulkRecordIssuesTest.java index 23ba2127c..f030fc72d 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/ISQLServerBulkRecordIssuesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/ISQLServerBulkRecordIssuesTest.java @@ -22,6 +22,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.text.MessageFormat; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; @@ -34,6 +35,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerBulkCopy; import com.microsoft.sqlserver.jdbc.SQLServerConnection; import com.microsoft.sqlserver.jdbc.SQLServerException; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; @@ -64,11 +66,11 @@ public void testVarchar() throws Exception { bcOperation.setDestinationTableName(destTable); bcOperation.writeToServer(bData); bcOperation.close(); - fail("BulkCopy executed for testVarchar when it it was expected to fail"); + fail(TestResource.getResource("R_expectedFailPassed")); } catch (Exception e) { if (e instanceof SQLException) { - assertTrue(e.getMessage().contains("The given value of type"), "Invalid Error message: " + e.toString()); + assertTrue(e.getMessage().contains(TestResource.getResource("R_givenValueType")), TestResource.getResource("R_invalidErrorMessage") + e.toString()); } else { fail(e.getMessage()); @@ -121,8 +123,11 @@ public void testSmalldatetimeOutofRange() throws Exception { } catch (Exception e) { if (e instanceof SQLException) { - assertTrue(e.getMessage().contains("Conversion failed when converting character string to smalldatetime data type"), - "Invalid Error message: " + e.toString()); + MessageFormat form = new MessageFormat(TestResource.getResource("R_conversionFailed")); + Object[] msgArgs = {"character string", "smalldatetime"}; + + assertTrue(e.getMessage().contains(form.format(msgArgs)), + TestResource.getResource("R_invalidErrorMessage") + e.toString()); } else { fail(e.getMessage()); @@ -145,11 +150,11 @@ public void testBinaryColumnAsByte() throws Exception { try (SQLServerBulkCopy bcOperation = new SQLServerBulkCopy(connectionString)) { bcOperation.setDestinationTableName(destTable); bcOperation.writeToServer(bData); - fail("BulkCopy executed for testBinaryColumnAsByte when it it was expected to fail"); + fail(TestResource.getResource("R_expectedFailPassed")); } catch (Exception e) { if (e instanceof SQLException) { - assertTrue(e.getMessage().contains("The given value of type"), "Invalid Error message: " + e.toString()); + assertTrue(e.getMessage().contains(TestResource.getResource("R_givenValueType")), TestResource.getResource("R_invalidErrorMessage") + e.toString()); } else { fail(e.getMessage()); @@ -172,11 +177,11 @@ public void testBinaryColumnAsString() throws Exception { try (SQLServerBulkCopy bcOperation = new SQLServerBulkCopy(connectionString)) { bcOperation.setDestinationTableName(destTable); bcOperation.writeToServer(bData); - fail("BulkCopy executed for testBinaryColumnAsString when it it was expected to fail"); + fail(TestResource.getResource("R_expectedFailPassed")); } catch (Exception e) { if (e instanceof SQLException) { - assertTrue(e.getMessage().contains("The given value of type"), "Invalid Error message: " + e.toString()); + assertTrue(e.getMessage().contains(TestResource.getResource("R_givenValueType")), TestResource.getResource("R_invalidErrorMessage") + e.toString()); } else { fail(e.getMessage()); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bvt/bvtTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bvt/bvtTest.java index eaad85365..d96044039 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bvt/bvtTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bvt/bvtTest.java @@ -29,6 +29,8 @@ import com.microsoft.sqlserver.testframework.DBResultSetTypes; import com.microsoft.sqlserver.testframework.DBStatement; +import com.microsoft.sqlserver.jdbc.TestResource;; + @RunWith(JUnitPlatform.class) @DisplayName("BVT Test") public class bvtTest extends bvtTestSetup { @@ -53,9 +55,10 @@ public void testConnection() throws SQLException { @Test public void testConnectionIsClosed() throws SQLException { try (DBConnection conn = new DBConnection(connectionString)) { - assertTrue(!conn.isClosed(), "BVT connection should not be closed"); + assertTrue(!conn.isClosed(), TestResource.getResource("R_connShouldNotBeClosed")); conn.close(); - assertTrue(conn.isClosed(), "BVT connection should not be open"); + assertTrue(conn.isClosed(), TestResource.getResource("R_connShouldNotBeOpen")); + } } @@ -70,10 +73,10 @@ public void testDriverNameAndDriverVersion() throws SQLException { DatabaseMetaData metaData = conn.getMetaData(); Pattern p = Pattern.compile(driverNamePattern); Matcher m = p.matcher(metaData.getDriverName()); - assertTrue(m.find(), "Driver name is not a correct format! "); + assertTrue(m.find(), TestResource.getResource("R_incorrectDriverNameFormat")); String[] parts = metaData.getDriverVersion().split("\\."); if (parts.length != 4) - assertTrue(true, "Driver version number should be four parts! "); + assertTrue(true, TestResource.getResource("R_incorrectDriverVewrsionFormat")); } } @@ -209,7 +212,7 @@ public void testStmtForwardOnlyUpdateable() throws SQLException { rs.verifyCurrentRow(table1); try { rs.previous(); - assertTrue(false, "Previous should have thrown an exception"); + assertTrue(false, TestResource.getResource("R_previousShouldThrow")); } catch (SQLException ex) { // expected exception @@ -402,7 +405,7 @@ public void testTwoResultsetsSameStmt() throws SQLException { rs1.next(); } catch (SQLException e) { - assertEquals(e.toString(), "com.microsoft.sqlserver.jdbc.SQLServerException: The result set is closed."); + assertEquals(e.getMessage(), TestResource.getResource("R_resultsetClosed")); } rs2.next(); rs2.verifyCurrentRow(table2); @@ -410,7 +413,7 @@ public void testTwoResultsetsSameStmt() throws SQLException { rs1.next(); } catch (SQLException e) { - assertEquals(e.toString(), "com.microsoft.sqlserver.jdbc.SQLServerException: The result set is closed."); + assertEquals(e.getMessage(), TestResource.getResource("R_resultsetClosed")); } rs1.close(); rs2.next(); @@ -436,7 +439,7 @@ public void testResultSetAndCloseStmt() throws SQLException { rs.next(); } catch (SQLException e) { - assertEquals(e.toString(), "com.microsoft.sqlserver.jdbc.SQLServerException: The result set is closed."); + assertEquals(e.getMessage(), TestResource.getResource("R_resultsetClosed")); } assertTrue(true, "Previous one should have thrown exception!"); } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java index 287786c0f..cbf4a0066 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java @@ -11,6 +11,7 @@ import java.sql.Statement; import java.sql.Types; import java.util.UUID; +import java.text.MessageFormat; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -20,6 +21,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerCallableStatement; import com.microsoft.sqlserver.jdbc.SQLServerDataSource; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; @@ -150,10 +152,14 @@ public void inputParamsTest() throws SQLException { CallableStatement cs3 = connection.prepareCall(call); try { cs3.setString("@whatever", "test"); - fail("SQLException should have been thrown"); + fail(TestResource.getResource("R_shouldThrowException")); } catch (SQLException sse) { - if (!sse.getMessage().startsWith("Parameter @whatever was not defined")) { - fail("Unexpected content in exception message"); + + MessageFormat form = new MessageFormat(TestResource.getResource("R_parameterNotDefined")); + Object[] msgArgs = {"@whatever"}; + + if (!sse.getMessage().startsWith(form.format(msgArgs))) { + fail(TestResource.getResource("R_unexpectedExceptionContent")); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionDriverTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionDriverTest.java index 64a982e6a..393ca2436 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionDriverTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionDriverTest.java @@ -25,6 +25,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.logging.Logger; +import java.text.MessageFormat; import javax.sql.ConnectionEvent; import javax.sql.PooledConnection; @@ -39,6 +40,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource; import com.microsoft.sqlserver.jdbc.SQLServerDataSource; import com.microsoft.sqlserver.jdbc.SQLServerDriver; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBConnection; import com.microsoft.sqlserver.testframework.DBTable; @@ -77,16 +79,16 @@ public void testConnectionDriver() throws SQLException { infoArray = d.getPropertyInfo(url.toString(), info); for (DriverPropertyInfo anInfoArray : infoArray) { if (anInfoArray.name.equals("encrypt")) { - assertTrue(anInfoArray.value.equals("true"), "Values are different"); + assertTrue(anInfoArray.value.equals("true"), TestResource.getResource("R_valuesAreDifferent")); } if (anInfoArray.name.equals("trustStore")) { - assertTrue(anInfoArray.value.equals("someStore"), "Values are different"); + assertTrue(anInfoArray.value.equals("someStore"), TestResource.getResource("R_valuesAreDifferent")); } if (anInfoArray.name.equals("trustStorePassword")) { - assertTrue(anInfoArray.value.equals("somepassword"), "Values are different"); + assertTrue(anInfoArray.value.equals("somepassword"), TestResource.getResource("R_valuesAreDifferent")); } if (anInfoArray.name.equals("hostNameInCertificate")) { - assertTrue(anInfoArray.value.equals("someHost"), "Values are different"); + assertTrue(anInfoArray.value.equals("someHost"), TestResource.getResource("R_valuesAreDifferent")); } } } @@ -109,9 +111,9 @@ public void testDataSource() { ds.setEncrypt(true); ds.setTrustStorePassword(trustStorePassword); ds.setTrustServerCertificate(true); - assertEquals(trustStore, ds.getTrustStore(), "Values are different"); - assertEquals(true, ds.getEncrypt(), "Values are different"); - assertEquals(true, ds.getTrustServerCertificate(), "Values are different"); + assertEquals(trustStore, ds.getTrustStore(), TestResource.getResource("R_valuesAreDifferent")); + assertEquals(true, ds.getEncrypt(), TestResource.getResource("R_valuesAreDifferent")); + assertEquals(true, ds.getTrustServerCertificate(), TestResource.getResource("R_valuesAreDifferent")); } @Test @@ -129,14 +131,14 @@ public void testEncryptedConnection() throws SQLException { public void testJdbcDriverMethod() throws SQLFeatureNotSupportedException { SQLServerDriver serverDriver = new SQLServerDriver(); Logger logger = serverDriver.getParentLogger(); - assertEquals(logger.getName(), "com.microsoft.sqlserver.jdbc", "Parent Logger name is wrong"); + assertEquals(logger.getName(), "com.microsoft.sqlserver.jdbc", TestResource.getResource("R_parrentLoggerNameWrong")); } @Test public void testJdbcDataSourceMethod() throws SQLFeatureNotSupportedException { SQLServerDataSource fxds = new SQLServerDataSource(); Logger logger = fxds.getParentLogger(); - assertEquals(logger.getName(), "com.microsoft.sqlserver.jdbc", "Parent Logger name is wrong"); + assertEquals(logger.getName(), "com.microsoft.sqlserver.jdbc", TestResource.getResource("R_parrentLoggerNameWrong")); } class MyEventListener implements javax.sql.ConnectionEventListener { @@ -162,7 +164,7 @@ public void connectionErrorOccurred(ConnectionEvent event) { */ @Test public void testConnectionEvents() throws SQLException { - assumeTrue(!DBConnection.isSqlAzure(DriverManager.getConnection(connectionString)), "Skipping test case on Azure SQL."); + assumeTrue(!DBConnection.isSqlAzure(DriverManager.getConnection(connectionString)), TestResource.getResource("R_skipAzure")); SQLServerConnectionPoolDataSource mds = new SQLServerConnectionPoolDataSource(); mds.setURL(connectionString); @@ -184,17 +186,17 @@ public void testConnectionEvents() throws SQLException { catch (Exception e) { exceptionThrown = true; } - assertTrue(exceptionThrown, "Expected exception is not thrown."); + assertTrue(exceptionThrown, TestResource.getResource("R_expectedExceptionNotThrown")); // Check to see if error occurred. - assertTrue(myE.errorOccurred, "Error occurred is not called."); + assertTrue(myE.errorOccurred, TestResource.getResource("R_errorNotCalled")); } // make sure that connection is closed. } @Test public void testConnectionPoolGetTwice() throws SQLException { - assumeTrue(!DBConnection.isSqlAzure(DriverManager.getConnection(connectionString)), "Skipping test case on Azure SQL."); + assumeTrue(!DBConnection.isSqlAzure(DriverManager.getConnection(connectionString)), TestResource.getResource("R_skipAzure")); SQLServerConnectionPoolDataSource mds = new SQLServerConnectionPoolDataSource(); mds.setURL(connectionString); @@ -209,18 +211,18 @@ public void testConnectionPoolGetTwice() throws SQLException { // raise a non severe exception and make sure that the connection is not closed. stmt.executeUpdate("RAISERROR ('foo', 3,1) WITH LOG"); // not a serious error there should not be any errors. - assertTrue(!myE.errorOccurred, "Error occurred is called."); + assertTrue(!myE.errorOccurred, TestResource.getResource("R_errorCalled")); // check to make sure that connection is not closed. - assertTrue(!con.isClosed(), "Connection is closed."); + assertTrue(!con.isClosed(), TestResource.getResource("R_connectionIsClosed")); stmt.close(); con.close(); // check to make sure that connection is closed. - assertTrue(con.isClosed(), "Connection is not closed."); + assertTrue(con.isClosed(), TestResource.getResource("R_connectionIsNotClosed")); } @Test public void testConnectionClosed() throws SQLException { - assumeTrue(!DBConnection.isSqlAzure(DriverManager.getConnection(connectionString)), "Skipping test case on Azure SQL."); + assumeTrue(!DBConnection.isSqlAzure(DriverManager.getConnection(connectionString)), TestResource.getResource("R_skipAzure")); SQLServerDataSource mds = new SQLServerDataSource(); mds.setURL(connectionString); @@ -234,10 +236,10 @@ public void testConnectionClosed() throws SQLException { catch (Exception e) { exceptionThrown = true; } - assertTrue(exceptionThrown, "Expected exception is not thrown."); + assertTrue(exceptionThrown, TestResource.getResource("R_expectedExceptionNotThrown")); // check to make sure that connection is closed. - assertTrue(con.isClosed(), "Connection is not closed."); + assertTrue(con.isClosed(), TestResource.getResource("R_connectionIsNotClosed")); } @Test @@ -246,13 +248,17 @@ public void testIsWrapperFor() throws SQLException, ClassNotFoundException { SQLServerConnection ssconn = (SQLServerConnection) conn) { boolean isWrapper; isWrapper = ssconn.isWrapperFor(ssconn.getClass()); - assertTrue(isWrapper, "SQLServerConnection supports unwrapping"); - assertEquals(ssconn.TRANSACTION_SNAPSHOT, ssconn.TRANSACTION_SNAPSHOT, "Cant access the TRANSACTION_SNAPSHOT "); + MessageFormat form = new MessageFormat(TestResource.getResource("R_supportUnwrapping")); + Object[] msgArgs1 = {"SQLServerConnection"}; + + assertTrue(isWrapper, form.format(msgArgs1)); + assertEquals(ssconn.TRANSACTION_SNAPSHOT, ssconn.TRANSACTION_SNAPSHOT, TestResource.getResource("R_cantAccessSnapshot")); isWrapper = ssconn.isWrapperFor(Class.forName("com.microsoft.sqlserver.jdbc.ISQLServerConnection")); - assertTrue(isWrapper, "ISQLServerConnection supports unwrapping"); + Object[] msgArgs2 = {"ISQLServerConnection"}; + assertTrue(isWrapper, form.format(msgArgs2)); ISQLServerConnection iSql = (ISQLServerConnection) ssconn.unwrap(Class.forName("com.microsoft.sqlserver.jdbc.ISQLServerConnection")); - assertEquals(iSql.TRANSACTION_SNAPSHOT, iSql.TRANSACTION_SNAPSHOT, "Cant access the TRANSACTION_SNAPSHOT "); + assertEquals(iSql.TRANSACTION_SNAPSHOT, iSql.TRANSACTION_SNAPSHOT, TestResource.getResource("R_cantAccessSnapshot")); ssconn.unwrap(Class.forName("java.sql.Connection")); } @@ -261,7 +267,7 @@ public void testIsWrapperFor() throws SQLException, ClassNotFoundException { @Test public void testNewConnection() throws SQLException { try(SQLServerConnection conn = (SQLServerConnection) DriverManager.getConnection(connectionString)) { - assertTrue(conn.isValid(0), "Newly created connection should be valid"); + assertTrue(conn.isValid(0), TestResource.getResource("R_newConnectionShouldBeValid")); } } @@ -269,7 +275,7 @@ public void testNewConnection() throws SQLException { public void testClosedConnection() throws SQLException { SQLServerConnection conn = (SQLServerConnection) DriverManager.getConnection(connectionString); conn.close(); - assertTrue(!conn.isValid(0), "Closed connection should be invalid"); + assertTrue(!conn.isValid(0), TestResource.getResource("R_closedConnectionShouldBeInvalid")); } @Test @@ -277,17 +283,20 @@ public void testNegativeTimeout() throws Exception { try (SQLServerConnection conn = (SQLServerConnection) DriverManager.getConnection(connectionString)) { try { conn.isValid(-42); - throw new Exception("No exception thrown with negative timeout"); + throw new Exception(TestResource.getResource("R_noExceptionNegativeTimeout")); } catch (SQLException e) { - assertEquals(e.getMessage(), "The query timeout value -42 is not valid.", "Wrong exception message"); + MessageFormat form = new MessageFormat(TestResource.getResource("R_invalidQueryTimeout")); + Object[] msgArgs = {"-42"}; + + assertEquals(e.getMessage(), form.format(msgArgs), TestResource.getResource("R_wrongExceptionMessage")); } } } @Test public void testDeadConnection() throws SQLException { - assumeTrue(!DBConnection.isSqlAzure(DriverManager.getConnection(connectionString)), "Skipping test case on Azure SQL."); + assumeTrue(!DBConnection.isSqlAzure(DriverManager.getConnection(connectionString)), TestResource.getResource("R_skipAzure")); try (SQLServerConnection conn = (SQLServerConnection) DriverManager.getConnection(connectionString + ";responseBuffering=adaptive")) { @@ -307,27 +316,27 @@ public void testDeadConnection() throws SQLException { + " as x2; RAISERROR ('Oops', 21, 42) WITH LOG"); } catch (SQLException e) { - assertEquals(e.getMessage(), "Connection reset", "Unknown Exception"); + assertEquals(e.getMessage(), TestResource.getResource("R_connectionReset"), TestResource.getResource("R_unknownException")); } finally { DriverManager.getConnection(connectionString).createStatement().execute("drop table " + tableName); } - assertEquals(conn.isValid(5), false, "Dead connection should be invalid"); + assertEquals(conn.isValid(5), false, TestResource.getResource("R_deadConnection")); } } @Test public void testClientConnectionId() throws Exception { SQLServerConnection conn = (SQLServerConnection) DriverManager.getConnection(connectionString); - assertTrue(conn.getClientConnectionId() != null, "ClientConnectionId is null"); + assertTrue(conn.getClientConnectionId() != null, TestResource.getResource("R_clientConnectionIdNull")); conn.close(); try { // Call getClientConnectionId on a closed connection, should raise exception conn.getClientConnectionId(); - throw new Exception("No exception thrown calling getClientConnectionId on a closed connection"); + throw new Exception(TestResource.getResource("R_noExceptionClosedConnection")); } catch (SQLException e) { - assertEquals(e.getMessage(), "The connection is closed.", "Wrong exception message"); + assertEquals(e.getMessage(), TestResource.getResource("R_connectionIsClosed"), TestResource.getResource("R_wrongExceptionMessage")); } conn = null; @@ -340,7 +349,7 @@ public void testClientConnectionId() throws Exception { } catch (SQLException e) { assertTrue(e.getMessage().indexOf("ClientConnectionId") != -1, - "Unexpected: ClientConnectionId is not in exception message due to wrong DB"); + TestResource.getResource("R_unexpectedWrongDB")); } try { @@ -352,7 +361,7 @@ public void testClientConnectionId() throws Exception { } catch (SQLException e) { assertEquals(false, e.getMessage().indexOf("ClientConnectionId") != -1, - "Unexpected: ClientConnectionId is in exception message due to wrong host"); + TestResource.getResource("R_unexpectedWrongHost")); } } @@ -371,13 +380,16 @@ public void testIncorrectDatabase() throws SQLException { con = ds.getConnection(); } catch (Exception e) { - assertTrue(e.getMessage().contains("Cannot open database")); + assertTrue(e.getMessage().contains(TestResource.getResource("R_cannotOpenDatabase"))); timerEnd = System.currentTimeMillis(); } long timeDiff = timerEnd - timerStart; - assertTrue(con == null, "Should not have connected."); - assertTrue(timeDiff <= milsecs, "Exited in more than " + (milsecs / 1000) + " seconds."); + assertTrue(con == null, TestResource.getResource("R_shouldNotConnect")); + + MessageFormat form = new MessageFormat(TestResource.getResource("R_exitedMoreSeconds")); + Object[] msgArgs = {milsecs / 1000}; + assertTrue(timeDiff <= milsecs, form.format(msgArgs)); } @Test @@ -395,13 +407,15 @@ public void testIncorrectUserName() throws SQLException { con = ds.getConnection(); } catch (Exception e) { - assertTrue(e.getMessage().contains("Login failed")); + assertTrue(e.getMessage().contains(TestResource.getResource("R_loginFailed"))); timerEnd = System.currentTimeMillis(); } long timeDiff = timerEnd - timerStart; - assertTrue(con == null, "Should not have connected."); - assertTrue(timeDiff <= milsecs, "Exited in more than " + (milsecs / 1000) + " seconds."); + assertTrue(con == null, TestResource.getResource("R_shouldNotConnect")); + MessageFormat form = new MessageFormat(TestResource.getResource("R_exitedMoreSeconds")); + Object[] msgArgs = {milsecs / 1000}; + assertTrue(timeDiff <= milsecs, form.format(msgArgs)); } @Test @@ -419,13 +433,15 @@ public void testIncorrectPassword() throws SQLException { con = ds.getConnection(); } catch (Exception e) { - assertTrue(e.getMessage().contains("Login failed")); + assertTrue(e.getMessage().contains(TestResource.getResource("R_loginFailed"))); timerEnd = System.currentTimeMillis(); } long timeDiff = timerEnd - timerStart; - assertTrue(con == null, "Should not have connected."); - assertTrue(timeDiff <= milsecs, "Exited in more than " + (milsecs / 1000) + " seconds."); + assertTrue(con == null, TestResource.getResource("R_shouldNotConnect")); + MessageFormat form = new MessageFormat(TestResource.getResource("R_exitedMoreSeconds")); + Object[] msgArgs = {milsecs / 1000}; + assertTrue(timeDiff <= milsecs, form.format(msgArgs)); } @Test @@ -444,13 +460,15 @@ public void testInvalidCombination() throws SQLException { con = ds.getConnection(); } catch (Exception e) { - assertTrue(e.getMessage().contains("Connecting to a mirrored")); + assertTrue(e.getMessage().contains(TestResource.getResource("R_connectMirrored"))); timerEnd = System.currentTimeMillis(); } long timeDiff = timerEnd - timerStart; - assertTrue(con == null, "Should not have connected."); - assertTrue(timeDiff <= milsecs, "Exited in more than " + (milsecs / 1000) + " seconds."); + assertTrue(con == null, TestResource.getResource("R_shouldNotConnect")); + MessageFormat form = new MessageFormat(TestResource.getResource("R_exitedMoreSeconds")); + Object[] msgArgs = {milsecs / 1000}; + assertTrue(timeDiff <= milsecs, form.format(msgArgs)); } @Test @@ -473,8 +491,10 @@ public void testIncorrectDatabaseWithFailoverPartner() throws SQLException { } long timeDiff = timerEnd - timerStart; - assertTrue(con == null, "Should not have connected."); - assertTrue(timeDiff >= ((loginTimeOutInSeconds - 1) * 1000), "Exited in less than " + (loginTimeOutInSeconds - 1) + " seconds."); + assertTrue(con == null, TestResource.getResource("R_shouldNotConnect")); + MessageFormat form = new MessageFormat(TestResource.getResource("R_exitedLessSeconds")); + Object[] msgArgs = {loginTimeOutInSeconds - 1}; + assertTrue(timeDiff >= ((loginTimeOutInSeconds - 1) * 1000), form.format(msgArgs)); } @Test @@ -484,7 +504,7 @@ public void testAbortBadParam() throws SQLException { conn.abort(null); } catch (SQLException e) { - assertTrue(e.getMessage().contains("The argument executor is not valid")); + assertTrue(e.getMessage().contains(TestResource.getResource("R_invalidArgumentExecutor"))); } } @@ -546,6 +566,6 @@ public void run() { executor.shutdownNow(); - assertTrue(isInterrupted, "Thread's interrupt status is not set."); + assertTrue(isInterrupted, TestResource.getResource("R_threadInterruptNotSet")); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/NativeMSSQLDataSourceTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/NativeMSSQLDataSourceTest.java index b7d0edc3f..e8ac9164b 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/NativeMSSQLDataSourceTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/NativeMSSQLDataSourceTest.java @@ -28,6 +28,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource; import com.microsoft.sqlserver.jdbc.SQLServerDataSource; import com.microsoft.sqlserver.jdbc.SQLServerXADataSource; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; @RunWith(JUnitPlatform.class) @@ -71,7 +72,7 @@ public void testDSTSPassword() throws ClassNotFoundException, IOException, SQLEx ds = testSerial(ds); try (Connection conn = ds.getConnection()) {} catch (SQLException e) { - assertEquals("The DataSource trustStore password needs to be set.", e.getMessage()); + assertEquals(TestResource.getResource("R_trustStorePasswordNotSet"), e.getMessage()); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/PoolingTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/PoolingTest.java index 428c7d0cf..cd58cac86 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/PoolingTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/PoolingTest.java @@ -31,6 +31,7 @@ import com.microsoft.sqlserver.jdbc.ISQLServerConnection; import com.microsoft.sqlserver.jdbc.SQLServerXADataSource; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBConnection; import com.microsoft.sqlserver.testframework.DBTable; @@ -71,11 +72,11 @@ public void testPooling() throws SQLException { } catch (SQLException e) { // make sure the temporary table is not found. - if (e.getMessage().startsWith("Invalid object name")) { + if (e.getMessage().startsWith(TestResource.getResource("R_invalidObjectName"))) { tempTableFileRemoved = true; } } - assertTrue(tempTableFileRemoved, "Temporary table is not removed."); + assertTrue(tempTableFileRemoved, TestResource.getResource("R_tempTAbleNotRemoved")); } @Test @@ -90,7 +91,7 @@ public void testConnectionPoolReget() throws SQLException { Connection con2 = pc.getConnection(); // assert that the first connection is closed. - assertTrue(con.isClosed(), "First connection is not closed"); + assertTrue(con.isClosed(), TestResource.getResource("R_firstConnectionNotClosed")); } @Test @@ -125,7 +126,7 @@ public void testConnectionPoolClose() throws SQLException { pc.close(); // assert that the first connection is closed. - assertTrue(con.isClosed(), "Connection is not closed with pool close"); + assertTrue(con.isClosed(), TestResource.getResource("R_connectionNotClosedWithPoolClose")); } @Test @@ -137,7 +138,7 @@ public void testConnectionPoolClientConnectionId() throws SQLException { ISQLServerConnection con = (ISQLServerConnection) pc.getConnection(); UUID Id1 = con.getClientConnectionId(); - assertTrue(Id1 != null, "Unexecepted: ClientConnectionId is null from Pool"); + assertTrue(Id1 != null, TestResource.getResource("R_connectionNotClosedWithPoolClose")); con.close(); // now reget the connection @@ -146,7 +147,7 @@ public void testConnectionPoolClientConnectionId() throws SQLException { UUID Id2 = con2.getClientConnectionId(); con2.close(); - assertEquals(Id1, Id2, "ClientConnection Ids from pool are not the same."); + assertEquals(Id1, Id2, TestResource.getResource("R_idFromPoolNotSame")); } /** diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/SSLProtocolTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/SSLProtocolTest.java index c1e7e3351..e5eced4ad 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/SSLProtocolTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/SSLProtocolTest.java @@ -16,12 +16,15 @@ import java.sql.DriverManager; import java.sql.Statement; +import java.text.MessageFormat; + import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; import com.microsoft.sqlserver.jdbc.SQLServerException; import com.microsoft.sqlserver.jdbc.StringUtils; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; /** @@ -51,7 +54,7 @@ public void testWithSupportedProtocols(String sslProtocol) throws Exception { // Some older versions of SQLServer might not have all the TLS protocol versions enabled. // Example, if the highest TLS version enabled in the server is TLSv1.1, // the connection will fail if we enable only TLSv1.2 - assertTrue(e.getMessage().contains("protocol version is not enabled or not supported by the client.")); + assertTrue(e.getMessage().contains(TestResource.getResource("R_noProtocolVersion"))); } } @@ -66,12 +69,14 @@ public void testWithUnSupportedProtocols(String sslProtocol) throws Exception { try { String url = connectionString + ";sslProtocol=" + sslProtocol; con = DriverManager.getConnection(url); - assertFalse(true, "Any protocol other than TLSv1, TLSv1.1, and TLSv1.2 should throw Exception"); + assertFalse(true, TestResource.getResource("R_protocolVersion")); } catch (SQLServerException e) { - assertTrue(true, "Should throw exception"); - String errMsg = "SSL Protocol " + sslProtocol + " label is not valid. Only TLS, TLSv1, TLSv1.1, and TLSv1.2 are supported."; - assertTrue(errMsg.equals(e.getMessage()), "Message should be from SQL Server resources : " + e.getMessage()); + assertTrue(true, TestResource.getResource("R_shouldThrowException")); + MessageFormat form = new MessageFormat(TestResource.getResource("R_invalidProtocolLabel")); + Object[] msgArgs = {sslProtocol}; + String errMsg = form.format(msgArgs); + assertTrue(errMsg.equals(e.getMessage()), TestResource.getResource("R_SQLServerResourceMessage") + e.getMessage()); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java index 8b9dc919e..2382dfeed 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java @@ -21,6 +21,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerConnection; import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; import com.microsoft.sqlserver.testframework.util.RandomUtil; @@ -42,10 +43,10 @@ public void testDefaultLoginTimeout() { DriverManager.getConnection("jdbc:sqlserver://" + randomServer + ";user=sa;password=pwd;"); } catch (Exception e) { - assertTrue(e.getMessage().contains("The TCP/IP connection to the host")); + assertTrue(e.getMessage().contains(TestResource.getResource("R_tcpipConnectionToHost"))); timerEnd = System.currentTimeMillis(); } - assertTrue(0 != timerEnd, "Should not have connected."); + assertTrue(0 != timerEnd, TestResource.getResource("R_shouldNotConnect")); long timeDiff = timerEnd - timerStart; assertTrue(timeDiff > 14000); @@ -62,10 +63,10 @@ public void testFailoverInstanceResolution() throws SQLException { + "\\foo;user=sa;password=pwd;"); } catch (Exception e) { - assertTrue(e.getMessage().contains("The TCP/IP connection to the host")); + assertTrue(e.getMessage().contains(TestResource.getResource("R_tcpipConnectionToHost"))); timerEnd = System.currentTimeMillis(); } - assertTrue(0 != timerEnd, "Should not have connected."); + assertTrue(0 != timerEnd, TestResource.getResource("R_shouldNotConnect")); long timeDiff = timerEnd - timerStart; assertTrue(timeDiff > 14000); @@ -84,7 +85,7 @@ public void testFOInstanceResolution2() throws SQLException { catch (Exception e) { timerEnd = System.currentTimeMillis(); } - assertTrue(0 != timerEnd, "Should not have connected."); + assertTrue(0 != timerEnd, TestResource.getResource("R_shouldNotConnect")); long timeDiff = timerEnd - timerStart; assertTrue(timeDiff > 14000); @@ -105,18 +106,18 @@ public void testQueryTimeout() throws Exception { try { conn.createStatement().execute("exec " + waitForDelaySPName); - throw new Exception("Exception for queryTimeout is not thrown."); + throw new Exception(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (Exception e) { if (!(e instanceof java.sql.SQLTimeoutException)) { throw e; } - assertEquals(e.getMessage(), "The query has timed out.", "Invalid exception message"); + assertEquals(e.getMessage(), TestResource.getResource("R_queryTimedOut"), TestResource.getResource("R_invalidExceptionMessage")); } try{ conn.createStatement().execute("SELECT @@version"); }catch (Exception e) { - fail("Unexpected error message occured! "+ e.toString() ); + fail(TestResource.getResource("R_unexpectedErrorMessage") + e.toString() ); } } @@ -136,18 +137,18 @@ public void testCancelQueryTimeout() throws Exception { try { SQLServerStatement statement = (SQLServerStatement) conn.createStatement(); statement.execute("exec " + waitForDelaySPName); - throw new Exception("Exception for queryTimeout is not thrown."); + throw new Exception(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (Exception e) { if (!(e instanceof java.sql.SQLTimeoutException)) { throw e; } - assertEquals(e.getMessage(), "The query has timed out.", "Invalid exception message"); + assertEquals(e.getMessage(), TestResource.getResource("R_queryTimedOut"), TestResource.getResource("R_invalidExceptionMessage")); } try{ conn.createStatement().execute("SELECT @@version"); }catch (Exception e) { - fail("Unexpected error message occured! "+ e.toString() ); + fail(TestResource.getResource("R_unexpectedErrorMessage") + e.toString() ); } } @@ -169,18 +170,18 @@ public void testCancelQueryTimeoutOnStatement() throws Exception { statement.setQueryTimeout(waitForDelaySeconds/2); statement.setCancelQueryTimeout(waitForDelaySeconds); statement.execute("exec " + waitForDelaySPName); - throw new Exception("Exception for queryTimeout is not thrown."); + throw new Exception(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (Exception e) { if (!(e instanceof java.sql.SQLTimeoutException)) { throw e; } - assertEquals(e.getMessage(), "The query has timed out.", "Invalid exception message"); + assertEquals(e.getMessage(), TestResource.getResource("R_queryTimedOut"), TestResource.getResource("R_invalidExceptionMessage")); } try{ conn.createStatement().execute("SELECT @@version"); }catch (Exception e) { - fail("Unexpected error message occured! "+ e.toString() ); + fail(TestResource.getResource("R_unexpectedErrorMessage") + e.toString() ); } } @@ -199,18 +200,18 @@ public void testSocketTimeout() throws Exception { try { conn.createStatement().execute("exec " + waitForDelaySPName); - throw new Exception("Exception for socketTimeout is not thrown."); + throw new Exception(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (Exception e) { if (!(e instanceof SQLException)) { throw e; } - assertEquals(e.getMessage(), "Read timed out", "Invalid exception message"); + assertEquals(e.getMessage(), TestResource.getResource("R_readTimedOut"), TestResource.getResource("R_invalidExceptionMessage")); } try{ conn.createStatement().execute("SELECT @@version"); }catch (SQLException e) { - assertEquals(e.getMessage(), "The connection is closed.", "Invalid exception message"); + assertEquals(e.getMessage(), TestResource.getResource("R_connectionIsClosed"), TestResource.getResource("R_invalidExceptionMessage")); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/WarningTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/WarningTest.java index 8b99699b1..6b3cccbf4 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/WarningTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/WarningTest.java @@ -22,6 +22,7 @@ import org.junit.runner.RunWith; import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; @RunWith(JUnitPlatform.class) @@ -51,7 +52,7 @@ public void testWarnings() throws SQLException { break; } } - assertTrue(found, "warning : '" + warn.toString() + "' not found!"); + assertTrue(found, TestResource.getResource("R_warningsNotFound") + ": " + warn.toString()); warn = warn.getNextWarning(); found = false; } @@ -59,8 +60,8 @@ public void testWarnings() throws SQLException { conn.setClientInfo("prop7", ""); warn = conn.getWarnings(); - assertTrue(warn.toString().contains("prop7"), "Warnings not found!"); + assertTrue(warn.toString().contains("prop7"), TestResource.getResource("R_warningsNotFound")); warn = warn.getNextWarning(); - assertEquals(null, warn, "Warnings found!"); + assertEquals(null, warn, TestResource.getResource("R_warningsFound")); } } 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 01a683117..403d876e2 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java @@ -25,6 +25,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerDatabaseMetaData; import com.microsoft.sqlserver.jdbc.SQLServerResultSet; import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; @@ -45,10 +46,6 @@ public class DatabaseMetaDataForeignKeyTest extends AbstractTest { private static String schema = null; private static String catalog = null; - private static final String EXPECTED_ERROR_MESSAGE = "An object or column name is missing or empty."; - private static final String EXPECTED_ERROR_MESSAGE2 = "The database name component of the object qualifier must be the name of the current database."; - - @BeforeAll private static void setupVariation() throws SQLException { conn = (SQLServerConnection) DriverManager.getConnection(connectionString); @@ -113,10 +110,10 @@ public void testGetImportedKeys() throws SQLException { try { dmd.getImportedKeys("", schema, table1); - fail("Exception is not thrown."); + fail(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (SQLException e) { - assertTrue(e.getMessage().startsWith(EXPECTED_ERROR_MESSAGE2)); + assertTrue(e.getMessage().startsWith(TestResource.getResource("R_dbNameIsCurrentDB"))); } } @@ -145,7 +142,7 @@ private void validateGetImportedKeysResults(SQLServerResultSet rs) throws SQLExc rowCount++; if(expectedRowCount != rowCount) { - assertEquals(expectedRowCount, rowCount, "number of foreign key entries is incorrect."); + assertEquals(expectedRowCount, rowCount, TestResource.getResource("R_numKeysIncorrect")); } } @@ -187,10 +184,10 @@ public void testGetExportedKeys() throws SQLException { try { dmd.getExportedKeys("", schema, pkTable); - fail("Exception is not thrown."); + fail(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (SQLException e) { - assertTrue(e.getMessage().startsWith(EXPECTED_ERROR_MESSAGE2)); + assertTrue(e.getMessage().startsWith(TestResource.getResource("R_dbNameIsCurrentDB"))); } } } @@ -234,11 +231,11 @@ public void testGetCrossReference() throws SQLException { try { dmd.getCrossReference("", schema, pkTable, "", schema, fkTable); - fail("Exception is not thrown."); + fail(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (SQLException e) { - assertEquals(EXPECTED_ERROR_MESSAGE2, e.getMessage()); + assertEquals(TestResource.getResource("R_dbNameIsCurrentDB"), e.getMessage()); } } } -} \ No newline at end of file +} 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 fdc3d917a..515dd9d62 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -25,6 +25,7 @@ import java.sql.SQLException; import java.util.jar.Attributes; import java.util.jar.Manifest; +import java.text.MessageFormat; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; @@ -32,6 +33,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerDatabaseMetaData; import com.microsoft.sqlserver.jdbc.StringUtils; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; @@ -73,7 +75,7 @@ public void testDriverVersion() throws SQLException, IOException { File f = new File(manifestFile); - assumeTrue(f.exists(), "Manifest file does not exist on classpath so ignoring test"); + assumeTrue(f.exists(), TestResource.getResource("R_manifestNotFound")); InputStream in = new BufferedInputStream(new FileInputStream(f)); Manifest manifest = new Manifest(in); @@ -99,10 +101,11 @@ public void testDriverVersion() throws SQLException, IOException { if (isSnapshot) { assertTrue(intDriverVersion < intBuildVersion, - "In case of SNAPSHOT version, build version should be always greater than BuildVersion"); + TestResource.getResource("R_buildVersionError")); } else { - assertTrue(intDriverVersion == intBuildVersion, "For NON SNAPSHOT versions build & driver versions should match."); + assertTrue(intDriverVersion == intBuildVersion, + TestResource.getResource("R_buildVersionError")); } } @@ -117,7 +120,7 @@ public void testGetURL() throws SQLException { DatabaseMetaData databaseMetaData = connection.getMetaData(); String url = databaseMetaData.getURL(); url = url.toLowerCase(); - assertFalse(url.contains("password"), "Get URL should not have password attribute / property."); + assertFalse(url.contains("password"), TestResource.getResource("R_getURLContainsPwd")); } /** @@ -150,10 +153,10 @@ else if (connectionString.contains("user")) { String userFromConnectionString = connectionString.substring(startIndex, endIndex); String userName = databaseMetaData.getUserName(); - assertNotNull(userName, "databaseMetaData.getUserName() should not be null"); + assertNotNull(userName, TestResource.getResource("R_userNameNull")); assertTrue(userName.equalsIgnoreCase(userFromConnectionString), - "databaseMetaData.getUserName() should match with UserName from Connection String."); + TestResource.getResource("R_userNameNotMatch")); } /** @@ -166,8 +169,10 @@ public void testDBSchema() throws SQLException { ResultSet rs = databaseMetaData.getSchemas(); + MessageFormat form = new MessageFormat(TestResource.getResource("R_nameEmpty")); + Object[] msgArgs = {"Schema"}; while (rs.next()) { - assertTrue(!StringUtils.isEmpty(rs.getString(1)), "Schema Name should not be Empty"); + assertTrue(!StringUtils.isEmpty(rs.getString(1)), form.format(msgArgs)); } } @@ -182,13 +187,17 @@ public void testDBTables() throws SQLException { ResultSet rsCatalog = databaseMetaData.getCatalogs(); - assertTrue(rsCatalog.next(), "We should get atleast one catalog"); + MessageFormat form1 = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); + Object[] msgArgs1 = {"catalog"}; + assertTrue(rsCatalog.next(), form1.format(msgArgs1)); String[] types = {"TABLE"}; 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")),"Table Name should not be Empty"); + assertTrue(!StringUtils.isEmpty(rs.getString("TABLE_NAME")), form2.format(msgArgs2)); } } @@ -208,22 +217,26 @@ public void testGetDBColumn() throws SQLException { ResultSet rs = databaseMetaData.getTables(null, null, "%", types); //Fetch one table - assertTrue(rs.next(), "At least one table should be found"); + MessageFormat form1 = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); + Object[] msgArgs1 = {"table"}; + assertTrue(rs.next(), form1.format(msgArgs1)); //Go through all columns. ResultSet rs1 = databaseMetaData.getColumns(null, null, rs.getString("TABLE_NAME"), "%"); + 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")), "Category Name should not be Empty"); // 1 - assertTrue(!StringUtils.isEmpty(rs1.getString("TABLE_SCHEM")), "SCHEMA Name should not be Empty"); - assertTrue(!StringUtils.isEmpty(rs1.getString("TABLE_NAME")), "Table Name should not be Empty"); - assertTrue(!StringUtils.isEmpty(rs1.getString("COLUMN_NAME")), "COLUMN NAME should not be Empty"); - assertTrue(!StringUtils.isEmpty(rs1.getString("DATA_TYPE")), "Data Type should not be Empty"); - assertTrue(!StringUtils.isEmpty(rs1.getString("TYPE_NAME")), "Data Type Name should not be Empty"); // 6 - assertTrue(!StringUtils.isEmpty(rs1.getString("COLUMN_SIZE")), "Column Size should not be Empty"); // 7 - assertTrue(!StringUtils.isEmpty(rs1.getString("NULLABLE")), "Nullable value should not be Empty"); // 11 - assertTrue(!StringUtils.isEmpty(rs1.getString("IS_NULLABLE")), "Nullable value should not be Empty"); // 18 - assertTrue(!StringUtils.isEmpty(rs1.getString("IS_AUTOINCREMENT")), "Nullable value should not be Empty"); // 22 + 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 } } @@ -242,21 +255,24 @@ public void testGetColumnPrivileges() throws SQLException { ResultSet rsTables = databaseMetaData.getTables(null, null, "%", types); //Fetch one table - assertTrue(rsTables.next(), "At least one table should be found"); + MessageFormat form1 = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); + Object[] msgArgs1 = {"table"}; + assertTrue(rsTables.next(), form1.format(msgArgs1)); //Go through all columns. ResultSet rs1 = databaseMetaData.getColumnPrivileges(null, null, rsTables.getString("TABLE_NAME"), "%"); + MessageFormat form2 = new MessageFormat(TestResource.getResource("R_nameEmpty")); + Object[][] msgArgs2 = {{"Category"}, {"SCHEMA"}, {"Table"}, {"COLUMN"}, {"GRANTOR"}, {"GRANTEE"}, {"PRIVILEGE"}, {"IS_GRANTABLE"}}; while(rs1.next()) { - assertTrue(!StringUtils.isEmpty(rs1.getString("TABLE_CAT")),"Category Name should not be Empty"); //1 - assertTrue(!StringUtils.isEmpty(rs1.getString("TABLE_SCHEM")),"SCHEMA Name should not be Empty"); - assertTrue(!StringUtils.isEmpty(rs1.getString("TABLE_NAME")),"Table Name should not be Empty"); - assertTrue(!StringUtils.isEmpty(rs1.getString("COLUMN_NAME")),"COLUMN NAME should not be Empty"); - assertTrue(!StringUtils.isEmpty(rs1.getString("GRANTOR")),"GRANTOR should not be Empty"); - assertTrue(!StringUtils.isEmpty(rs1.getString("GRANTEE")),"GRANTEE should not be Empty"); - assertTrue(!StringUtils.isEmpty(rs1.getString("PRIVILEGE")),"PRIVILEGE should not be Empty"); - assertTrue(!StringUtils.isEmpty(rs1.getString("IS_GRANTABLE")),"IS_GRANTABLE should be YES / NO"); - + 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("GRANTOR")), form2.format(msgArgs2[4])); + assertTrue(!StringUtils.isEmpty(rs1.getString("GRANTEE")), form2.format(msgArgs2[5])); + assertTrue(!StringUtils.isEmpty(rs1.getString("PRIVILEGE")), form2.format(msgArgs2[6])); + assertTrue(!StringUtils.isEmpty(rs1.getString("IS_GRANTABLE")), form2.format(msgArgs2[7])); } } @@ -271,8 +287,8 @@ public void testGetFunctionsWithWrongParams() throws SQLException { try { DatabaseMetaData databaseMetaData = connection.getMetaData(); databaseMetaData.getFunctions("", null, "xp_%"); - assertTrue(false,"As we are not supplying schema it should fail."); - }catch(Exception ae) { + assertTrue(false, TestResource.getResource("R_noSchemaShouldFail")); + } catch(Exception ae) { } } @@ -286,14 +302,16 @@ public void testGetFunctions() throws SQLException { DatabaseMetaData databaseMetaData = connection.getMetaData(); ResultSet rs = databaseMetaData.getFunctions(null, null, "xp_%"); + MessageFormat form = new MessageFormat(TestResource.getResource("R_nameNull")); + Object[][] msgArgs = {{"FUNCTION_CAT"}, {"FUNCTION_SCHEM"}, {"FUNCTION_NAME"}, {"NUM_INPUT_PARAMS"}, {"NUM_OUPUT_PARAMS"}, {"NUM_RESULT_SETS"}, {"FUNCTION_TYPE"}}; while(rs.next()) { - assertTrue(!StringUtils.isEmpty(rs.getString("FUNCTION_CAT")),"FUNCTION_CAT should not be NULL"); - assertTrue(!StringUtils.isEmpty(rs.getString("FUNCTION_SCHEM")),"FUNCTION_SCHEM should not be NULL"); - assertTrue(!StringUtils.isEmpty(rs.getString("FUNCTION_NAME")),"FUNCTION_NAME should not be NULL"); - assertTrue(!StringUtils.isEmpty(rs.getString("NUM_INPUT_PARAMS")),"NUM_INPUT_PARAMS should not be NULL"); - assertTrue(!StringUtils.isEmpty(rs.getString("NUM_OUTPUT_PARAMS")),"NUM_OUTPUT_PARAMS should not be NULL"); - assertTrue(!StringUtils.isEmpty(rs.getString("NUM_RESULT_SETS")),"NUM_RESULT_SETS should not be NULL"); - assertTrue(!StringUtils.isEmpty(rs.getString("FUNCTION_TYPE")),"FUNCTION_TYPE should not be NULL"); + assertTrue(!StringUtils.isEmpty(rs.getString("FUNCTION_CAT")), form.format(msgArgs[0])); + assertTrue(!StringUtils.isEmpty(rs.getString("FUNCTION_SCHEM")), form.format(msgArgs[1])); + assertTrue(!StringUtils.isEmpty(rs.getString("FUNCTION_NAME")), form.format(msgArgs[2])); + assertTrue(!StringUtils.isEmpty(rs.getString("NUM_INPUT_PARAMS")), form.format(msgArgs[3])); + assertTrue(!StringUtils.isEmpty(rs.getString("NUM_OUTPUT_PARAMS")), form.format(msgArgs[4])); + assertTrue(!StringUtils.isEmpty(rs.getString("NUM_RESULT_SETS")), form.format(msgArgs[5])); + assertTrue(!StringUtils.isEmpty(rs.getString("FUNCTION_TYPE")), form.format(msgArgs[6])); } rs.close(); } @@ -307,22 +325,26 @@ public void testGetFunctionColumns() throws SQLException{ DatabaseMetaData databaseMetaData = connection.getMetaData(); ResultSet rsFunctions = databaseMetaData.getFunctions(null, null, "%"); - //Fetch one Function - assertTrue(rsFunctions.next(), "At least one function should be found"); + //Fetch one Function + MessageFormat form1 = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); + Object[] msgArgs1 = {"function"}; + assertTrue(rsFunctions.next(), form1.format(msgArgs1)); //Go through all columns. ResultSet rs = databaseMetaData.getFunctionColumns(null, null, rsFunctions.getString("FUNCTION_NAME"), "%"); + MessageFormat form2 = new MessageFormat(TestResource.getResource("R_nameNull")); + Object[][] msgArgs2 = {{"FUNCTION_CAT"}, {"FUNCTION_SCHEM"}, {"FUNCTION_NAME"}, {"COLUMN_NAME"}, {"COLUMN_TYPE"}, {"DATA_TYPE"}, {"TYPE_NAME"}, {"NULLABLE"}, {"IS_NULLABLE"}}; while(rs.next()) { - assertTrue(!StringUtils.isEmpty(rs.getString("FUNCTION_CAT")),"FUNCTION_CAT should not be NULL"); - assertTrue(!StringUtils.isEmpty(rs.getString("FUNCTION_SCHEM")),"FUNCTION_SCHEM should not be NULL"); - assertTrue(!StringUtils.isEmpty(rs.getString("FUNCTION_NAME")),"FUNCTION_NAME should not be NULL"); - assertTrue(!StringUtils.isEmpty(rs.getString("COLUMN_NAME")),"COLUMN_NAME should not be NULL"); - assertTrue(!StringUtils.isEmpty(rs.getString("COLUMN_TYPE")),"COLUMN_TYPE should not be NULL"); - assertTrue(!StringUtils.isEmpty(rs.getString("DATA_TYPE")),"DATA_TYPE should not be NULL"); - assertTrue(!StringUtils.isEmpty(rs.getString("TYPE_NAME")),"TYPE_NAME should not be NULL"); - assertTrue(!StringUtils.isEmpty(rs.getString("NULLABLE")),"NULLABLE should not be NULL"); //12 - assertTrue(!StringUtils.isEmpty(rs.getString("IS_NULLABLE")),"IS_NULLABLE should not be NULL"); //19 + assertTrue(!StringUtils.isEmpty(rs.getString("FUNCTION_CAT")), form2.format(msgArgs2[0])); + assertTrue(!StringUtils.isEmpty(rs.getString("FUNCTION_SCHEM")), form2.format(msgArgs2[1])); + assertTrue(!StringUtils.isEmpty(rs.getString("FUNCTION_NAME")), form2.format(msgArgs2[2])); + assertTrue(!StringUtils.isEmpty(rs.getString("COLUMN_NAME")), form2.format(msgArgs2[3])); + assertTrue(!StringUtils.isEmpty(rs.getString("COLUMN_TYPE")), form2.format(msgArgs2[4])); + assertTrue(!StringUtils.isEmpty(rs.getString("DATA_TYPE")), form2.format(msgArgs2[5])); + assertTrue(!StringUtils.isEmpty(rs.getString("TYPE_NAME")), form2.format(msgArgs2[6])); + assertTrue(!StringUtils.isEmpty(rs.getString("NULLABLE")), form2.format(msgArgs2[7])); //12 + assertTrue(!StringUtils.isEmpty(rs.getString("IS_NULLABLE")), form2.format(msgArgs2[8])); //19 } } 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 71c2b68e3..886cb36cc 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLServerSpatialDatatypeTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLServerSpatialDatatypeTest.java @@ -31,9 +31,11 @@ import com.microsoft.sqlserver.jdbc.SQLServerConnection; import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; import com.microsoft.sqlserver.jdbc.SQLServerResultSet; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; + /** * Test Geometry / Geography classes * @@ -338,7 +340,7 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "Illegal character at wkt position 14"); + assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")+"14"); } //Too many closing bracket @@ -348,7 +350,7 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "Illegal character at wkt position 91"); + assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")+"91"); } //Too many opening bracket @@ -358,7 +360,7 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "Illegal character at wkt position 15"); + assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")+"15"); } //Too many coordinates @@ -368,7 +370,7 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "Illegal character at wkt position 23"); + assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")+"23"); } //Too little coordinates @@ -378,7 +380,7 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "Illegal character at wkt position 17"); + assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")+"17"); } //Incorrect data type @@ -388,7 +390,7 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "Illegal character at wkt position 14"); + assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")+"14"); } // too many commas @@ -398,7 +400,7 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "Illegal character at wkt position 35"); + assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")+"35"); } // too little commas @@ -408,7 +410,7 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "Illegal character at wkt position 35"); + assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")+"35"); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantResultSetTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantResultSetTest.java index 63dcfa18d..76701bba2 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantResultSetTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantResultSetTest.java @@ -30,6 +30,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerException; import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; import com.microsoft.sqlserver.jdbc.SQLServerResultSet; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; import com.microsoft.sqlserver.testframework.util.RandomData; @@ -831,7 +832,7 @@ public void testUnsupportedDatatype() throws SQLException { rs.next(); try { rs.getObject(1); - fail("Should have thrown unssuported tds type exception"); + fail(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (Exception e) { assertTrue(e.getMessage().equalsIgnoreCase("Unexpected TDS type DATETIMEOFFSETN in SQL_VARIANT.")); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/TVPWithSqlVariantTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/TVPWithSqlVariantTest.java index d51d7455f..e62a18dce 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/TVPWithSqlVariantTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/TVPWithSqlVariantTest.java @@ -31,6 +31,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; import com.microsoft.sqlserver.jdbc.SQLServerResultSet; import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; import com.microsoft.sqlserver.testframework.sqlType.SqlDate; @@ -302,7 +303,8 @@ public void testLongVarChar() throws SQLException { assertTrue(e.getMessage().contains("SQL_VARIANT does not support string values of length greater than 8000.")); } catch (Exception e) { - fail("Test should have failed! mistakenly inserted string value of more than 8000 in sql-variant"); + // Test should have failed! mistakenly inserted string value of more than 8000 in sql-variant + fail(TestResource.getResource("R_unexpectedException")); } finally { if (null != pstmt) { @@ -522,4 +524,4 @@ public static void afterAll() throws SQLException { } -} \ No newline at end of file +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/exception/ExceptionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/exception/ExceptionTest.java index 422a30d25..b21430515 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/exception/ExceptionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/exception/ExceptionTest.java @@ -13,6 +13,7 @@ import java.net.SocketTimeoutException; import java.sql.DriverManager; import java.sql.SQLException; +import java.text.MessageFormat; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; @@ -20,6 +21,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerBulkCSVFileRecord; import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; @@ -44,8 +46,10 @@ public void testBulkCSVFileRecordExceptionCause() throws Exception { throw e; } - assertTrue(null != e.getCause(), "Cause should not be null."); - assertTrue(e.getCause() instanceof UnsupportedEncodingException, "Cause should be instance of UnsupportedEncodingException."); + assertTrue(null != e.getCause(), TestResource.getResource("R_causeShouldNotBeNull")); + MessageFormat form = new MessageFormat(TestResource.getResource("R_causeShouldBeInstance")); + Object[] msgArgs = {"UnsupportedEncodingException"}; + assertTrue(e.getCause() instanceof UnsupportedEncodingException, form.format(msgArgs)); } } @@ -71,15 +75,17 @@ public void testSocketTimeoutExceptionCause() throws Exception { try { conn.createStatement().execute("exec " + waitForDelaySPName); - throw new Exception("Exception for socketTimeout is not thrown."); + throw new Exception(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (Exception e) { if (!(e instanceof SQLException)) { throw e; } - assertTrue(null != e.getCause(), "Cause should not be null."); - assertTrue(e.getCause() instanceof SocketTimeoutException, "Cause should be instance of SocketTimeoutException."); + assertTrue(null != e.getCause(), TestResource.getResource("R_causeShouldNotBeNull")); + MessageFormat form = new MessageFormat(TestResource.getResource("R_causeShouldBeInstance")); + Object[] msgArgs = {"SocketTimeoutException"}; + assertTrue(e.getCause() instanceof SocketTimeoutException, form.format(msgArgs)); } } finally { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsEnvTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsEnvTest.java index 41ddfd4c9..0b9c25b3d 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsEnvTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsEnvTest.java @@ -25,6 +25,8 @@ import com.microsoft.sqlserver.testframework.Utils; +import com.microsoft.sqlserver.jdbc.TestResource;; + /** * Class which will useful for checking if FIPS env. set or not. * @@ -71,21 +73,21 @@ public static void populateProperties() { */ @Test public void testFIPSOnOracle() throws Exception { - assumeTrue(ORACLE_JVM.equals(currentJVM), "Aborting test: As this is not Oracle Env. "); + assumeTrue(ORACLE_JVM.equals(currentJVM), TestResource.getResource("R_wrongEnv") + ORACLE_JVM); - assumeTrue("FIPS".equals(Utils.getConfiguredProperty("FIPS_ENV")), "Aborting test case as FIPS_ENV property is not set. "); + assumeTrue("FIPS".equals(Utils.getConfiguredProperty("FIPS_ENV")), TestResource.getResource("R_fipsPropertyNotSet")); - assertTrue(isFIPS("SunJSSE"), "FIPS Should be enabled"); + assertTrue(isFIPS("SunJSSE"), "FIPS " + TestResource.getResource("R_shouldBeEnabled")); // As JDK 1.7 is not supporting lambda for time being commenting. /* * assumingThat("NSSFIPS".equals(Utils.getConfiguredProperty("FIPS_ENV")), () -> assertAll("All FIPS", () -> assertTrue(isFIPS("SunJSSE"), - * "FIPS should be Enabled."), () -> assertTrue(isFIPS("SunPKCS11-NSS"), "Testing"))); + * TestResource.getResource("R_shouldBeEnabled")), () -> assertTrue(isFIPS("SunPKCS11-NSS"), "Testing"))); * * assumingThat("BCFIPS".equals(Utils.getConfiguredProperty("FIPS_ENV")), () -> assertAll("All FIPS", () -> assertTrue(isFIPS("SunJSSE"), - * "FIPS should be Enabled."), () -> assertTrue(isFIPS("BCFIPS"), "Testing"))); + * TestResource.getResource("R_shouldBeEnabled")), () -> assertTrue(isFIPS("BCFIPS"), "Testing"))); * - * assumingThat("FIPS".equals(Utils.getConfiguredProperty("FIPS_ENV")), ()-> assertTrue(isFIPS("SunJSSE"), "FIPS Should be enabled")); + * assumingThat("FIPS".equals(Utils.getConfiguredProperty("FIPS_ENV")), ()-> assertTrue(isFIPS("SunJSSE"), TestResource.getResource("R_shouldBeEnabled"))); */ } @@ -96,11 +98,11 @@ public void testFIPSOnOracle() throws Exception { */ @Test public void testFIPSOnIBM() throws Exception { - assumeTrue(IBM_JVM.equals(currentJVM), "Aborting test: As this is not IBM Env. "); + assumeTrue(IBM_JVM.equals(currentJVM), TestResource.getResource("R_wrongEnv") + IBM_JVM); - assumeTrue("FIPS".equals(Utils.getConfiguredProperty("FIPS_ENV")), "Aborting test case as FIPS is not enabled. "); + assumeTrue("FIPS".equals(Utils.getConfiguredProperty("FIPS_ENV")), TestResource.getResource("R_fipsPropertyNotSet")); - assertTrue(isFIPS("IBMJCEFIP"), "FIPS Should be enabled"); + assertTrue(isFIPS("IBMJCEFIP"), "FIPS " + TestResource.getResource("R_shouldBeEnabled")); // As JDK 1.7 is not supporting lambda for time being commenting. /* @@ -120,7 +122,7 @@ public void testFIPSOnIBM() throws Exception { @Test @Disabled public void testFIPSEnv() { - assumeTrue("FIPS".equals(Utils.getConfiguredProperty("FIPS_ENV")), "Aborting test: This is FIPS Enabled JVM"); + assumeTrue("FIPS".equals(Utils.getConfiguredProperty("FIPS_ENV")), TestResource.getResource("R_fipsPropertyNotSet")); // As JDK 1.7 is not supporting lambda for time being commenting. /* diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsTest.java index 66b88933f..67378152c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsTest.java @@ -22,6 +22,8 @@ import com.microsoft.sqlserver.testframework.PrepUtil; import com.microsoft.sqlserver.testframework.Utils; +import com.microsoft.sqlserver.jdbc.TestResource;; + /** * Test class for testing FIPS property settings. */ @@ -48,12 +50,12 @@ public void fipsTrustServerCertificateTest() throws Exception { Properties props = buildConnectionProperties(); props.setProperty("TrustServerCertificate", "true"); Connection con = PrepUtil.getConnection(connectionString, props); - Assertions.fail("It should fail as we are not passing appropriate params"); + Assertions.fail(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (SQLException e) { Assertions.assertTrue( - e.getMessage().contains("Unable to verify FIPS mode settings."), - "Should create exception for invalid TrustServerCertificate value"); + e.getMessage().contains(TestResource.getResource("R_invalidFipsConfig")), + TestResource.getResource("R_invalidTrustCert")); } } @@ -68,12 +70,12 @@ public void fipsEncryptTest() throws Exception { Properties props = buildConnectionProperties(); props.setProperty("encrypt", "false"); Connection con = PrepUtil.getConnection(connectionString, props); - Assertions.fail("It should fail as we are not passing appropriate params"); + Assertions.fail(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (SQLException e) { Assertions.assertTrue( - e.getMessage().contains("Unable to verify FIPS mode settings."), - "Should create exception for invalid encrypt value"); + e.getMessage().contains(TestResource.getResource("R_invalidFipsConfig")), + TestResource.getResource("R_invalidEncrypt")); } } @@ -123,12 +125,12 @@ public void fipsDatSourceEncrypt() { ds.setEncrypt(false); Connection con = ds.getConnection(); - Assertions.fail("It should fail as we are not passing appropriate params"); + Assertions.fail(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (SQLException e) { Assertions.assertTrue( - e.getMessage().contains("Unable to verify FIPS mode settings."), - "Should create exception for invalid encrypt value"); + e.getMessage().contains(TestResource.getResource("R_invalidFipsConfig")), + TestResource.getResource("R_invalidEncrypt")); } } @@ -144,12 +146,12 @@ public void fipsDataSourceTrustServerCertificateTest() throws Exception { setDataSourceProperties(ds); ds.setTrustServerCertificate(true); Connection con = ds.getConnection(); - Assertions.fail("It should fail as we are not passing appropriate params"); + Assertions.fail(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (SQLException e) { Assertions.assertTrue( - e.getMessage().contains("Unable to verify FIPS mode settings."), - "Should create exception for invalid TrustServerCertificate value"); + e.getMessage().contains(TestResource.getResource("R_invalidFipsConfig")), + TestResource.getResource("R_invalidTrustCert")); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java index c1c12a8d9..4db2eec83 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java @@ -26,6 +26,7 @@ import org.opentest4j.TestAbortedException; import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBConnection; import com.microsoft.sqlserver.testframework.Utils; @@ -80,14 +81,14 @@ public void testAddBatch2() throws SQLException { updateCount = pstmt.executeBatch(); updateCountlen += updateCount.length; - assertTrue(updateCountlen == 5, "addBatch does not add the SQL Statements to Batch ,call to addBatch failed"); + assertTrue(updateCountlen == 5, TestResource.getResource("R_addBatchFailed")); String sPrepStmt1 = "select count(*) from esimple"; pstmt1 = connection.prepareStatement(sPrepStmt1); rs = pstmt1.executeQuery(); rs.next(); - assertTrue(rs.getInt(1) == 5, "affected rows does not match with batch size. Insert failed"); + assertTrue(rs.getInt(1) == 5, TestResource.getResource("R_insertBatchFailed")); pstmt1.close(); } @@ -106,7 +107,7 @@ public void testAddbatch2AEOnConnection() throws SQLException { @BeforeEach public void testSetup() throws TestAbortedException, Exception { assumeTrue(13 <= new DBConnection(connectionString).getServerVersion(), - "Aborting test case as SQL Server version is not compatible with Always encrypted "); + TestResource.getResource("R_Incompat_SQLServerVersion")); connection = DriverManager.getConnection(connectionString); SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); @@ -139,4 +140,4 @@ public static void terminateVariation() throws SQLException { connection.close(); } } -} \ No newline at end of file +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java index 05f36bdee..880ed7ef7 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java @@ -25,6 +25,7 @@ import org.junit.runner.RunWith; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; +import com.microsoft.sqlserver.jdbc.TestResource; /** * Tests with sql queries using preparedStatement without parameters @@ -68,7 +69,7 @@ public void createViewTest() throws SQLException { pstmt2.execute(); } catch (SQLException e) { - fail("Create/drop view with preparedStatement failed! Error message: " + e.getMessage()); + fail(TestResource.getResource("R_createDropViewFailed") + TestResource.getResource("R_errorMessage") + e.getMessage()); } finally { @@ -95,7 +96,7 @@ public void createSchemaTest() throws SQLException { pstmt2.execute(); } catch (SQLException e) { - fail("Create/drop schema with preparedStatement failed! Error message:" + e.getMessage()); + fail(TestResource.getResource("R_createDropSchemaFailed") + TestResource.getResource("R_errorMessage") + e.getMessage()); } finally { @@ -122,7 +123,7 @@ public void createTableTest() throws SQLException { pstmt2.execute(); } catch (SQLException e) { - fail("Create/drop table with preparedStatement failed! Error message:" + e.getMessage()); + fail(TestResource.getResource("R_createDropTableFailed") + TestResource.getResource("R_errorMessage") + e.getMessage()); } finally { @@ -151,7 +152,7 @@ public void alterTableTest() throws SQLException { pstmt3.execute(); } catch (SQLException e) { - fail("Create/drop/alter table with preparedStatement failed! Error message:" + e.getMessage()); + fail(TestResource.getResource("R_createDropAlterTableFailed") + TestResource.getResource("R_errorMessage") + e.getMessage()); } finally { @@ -185,7 +186,7 @@ public void grantTest() throws SQLException { pstmt4.execute(); } catch (SQLException e) { - fail("grant with preparedStatement failed! Error message:" + e.getMessage()); + fail(TestResource.getResource("R_grantFailed") + TestResource.getResource("R_errorMessage") + e.getMessage()); } finally { @@ -433,4 +434,4 @@ public static void cleanup() throws SQLException { } -} \ No newline at end of file +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPIssuesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPIssuesTest.java index 41118d594..dfe8a5cd2 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPIssuesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPIssuesTest.java @@ -29,6 +29,8 @@ import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; +import com.microsoft.sqlserver.jdbc.TestResource;; + @RunWith(JUnitPlatform.class) public class TVPIssuesTest extends AbstractTest { @@ -78,11 +80,11 @@ public void testExceptionWithInvalidStoredProcedureName() throws Exception { SQLServerCallableStatement Cstmt = (SQLServerCallableStatement) connection.prepareCall(sql); try { Cstmt.setObject(1, rs); - throw new Exception("Expected Exception for invalied stored procedure name is not thrown."); + throw new Exception(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (Exception e) { if (e instanceof SQLException) { - assertTrue(e.getMessage().contains("Could not find stored procedure"), "Invalid Error Message."); + assertTrue(e.getMessage().contains(TestResource.getResource("R_StoredProcedureNotFound")), TestResource.getResource("R_invalidErrorMessage") + e.toString()); } else { throw e; @@ -114,7 +116,7 @@ public void tryTVPPrecisionmissedissue315() throws Exception { private void testCharDestTable() throws SQLException, IOException { ResultSet rs = connection.createStatement().executeQuery("select * from " + desTable_varcharMax); while (rs.next()) { - assertEquals(rs.getString(1).length(), 4001, " The inserted length is truncated or not correct!"); + assertEquals(rs.getString(1).length(), 4001, TestResource.getResource("R_lengthTruncated")); } if (null != rs) { rs.close(); @@ -124,7 +126,7 @@ private void testCharDestTable() throws SQLException, IOException { private void testTime6DestTable() throws SQLException, IOException { ResultSet rs = connection.createStatement().executeQuery("select * from " + desTable_time_6); while (rs.next()) { - assertEquals(rs.getString(1), expectedTime6value, " The time value is truncated or not correct!"); + assertEquals(rs.getString(1), expectedTime6value, TestResource.getResource("R_timeValueTruncated")); } if (null != rs) { rs.close(); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPResultSetCursorTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPResultSetCursorTest.java index 8dbd7e368..2557031fb 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPResultSetCursorTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPResultSetCursorTest.java @@ -27,6 +27,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerCallableStatement; import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; @@ -204,7 +205,7 @@ public void testInvalidTVPName() throws SQLException { pstmt.execute(); } catch (SQLException e) { - if (!e.getMessage().contains("Cannot find data type")) { + if (!e.getMessage().contains(TestResource.getResource("R_dataTypeNotFound"))) { throw e; } } @@ -251,7 +252,7 @@ public void testInvalidStoredProcedureName() throws SQLException { pstmt.execute(); } catch (SQLException e) { - if (!e.getMessage().contains("Could not find stored procedure")) { + if (!e.getMessage().contains(TestResource.getResource("R_StoredProcedureNotFound"))) { throw e; } } @@ -420,4 +421,4 @@ private void terminateVariation() throws SQLException { } } -} \ No newline at end of file +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPSchemaTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPSchemaTest.java index 7182646b3..dd029ee1b 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPSchemaTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPSchemaTest.java @@ -7,7 +7,7 @@ */ package com.microsoft.sqlserver.jdbc.tvp; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.sql.SQLException; @@ -167,14 +167,9 @@ private void verify(DBResultSet rs) throws SQLException { String actualValue2 = rs.getString(2); String actualValue3 = rs.getString(3); - assertTrue(actualValue1.trim().equals(expectecValue1), - "actual value does not match expected value." + "\n\tExpected value: " + expectecValue1 + "\n\tActual value: " + actualValue1); - - assertTrue(actualValue2.trim().equals(expectecValue2), - "actual value does not match expected value." + "\n\tExpected value: " + expectecValue2 + "\n\tActual value: " + actualValue2); - - assertTrue(actualValue3.trim().equals(expectecValue3), - "actual value does not match expected value." + "\n\tExpected value: " + expectecValue3 + "\n\tActual value: " + actualValue3); + assertEquals(actualValue1.trim(), expectecValue1); + assertEquals(actualValue2.trim(), expectecValue2); + assertEquals(actualValue3.trim(), expectecValue3); } } @@ -233,4 +228,4 @@ private void terminateVariation() throws SQLException { } } -} \ No newline at end of file +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/TestSavepoint.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/TestSavepoint.java index 34db2491f..18e7d97bf 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/TestSavepoint.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/TestSavepoint.java @@ -15,12 +15,14 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; +import java.text.MessageFormat; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; import com.microsoft.sqlserver.jdbc.SQLServerSavepoint; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.util.RandomUtil; @@ -44,14 +46,16 @@ public void testSavePointName() throws SQLException { connection.setAutoCommit(false); SQLServerSavepoint savePoint = (SQLServerSavepoint) connection.setSavepoint(savePointName); - assertTrue(savePointName.equals(savePoint.getSavepointName()), "Savepoint Name should be same."); + MessageFormat form = new MessageFormat(TestResource.getResource("R_savePointError")); + Object[][] msgArgs = {{"Name", "same"}, {"Label", "Savepoint Name"}, {"SQLServerSavepoint.isNamed", "true"}}; - assertTrue(savePointName.equals(savePoint.getLabel()), "Savepoint Label should be same as Savepoint Name."); + assertTrue(savePointName.equals(savePoint.getSavepointName()), form.format(msgArgs[0])); + assertTrue(savePointName.equals(savePoint.getLabel()), form.format(msgArgs[1])); + assertTrue(savePoint.isNamed(), form.format(msgArgs[2])); - assertTrue(savePoint.isNamed(), "SQLServerSavepoint.isNamed should be true"); try { savePoint.getSavepointId(); - assertTrue(false, "Expecting Exception as trying to get SavePointId when we created savepoint with name"); + assertTrue(false, TestResource.getResource("R_expectedExceptionNotThrown")); } catch (SQLException e) { } @@ -71,16 +75,21 @@ public void testSavePointId() throws SQLException { connection.setAutoCommit(false); SQLServerSavepoint savePoint = (SQLServerSavepoint) connection.setSavepoint(null); - assertNotNull(savePoint.getLabel(), "Savepoint Label should not be null."); + + MessageFormat form = new MessageFormat(TestResource.getResource("R_savePointError")); + Object[][] msgArgs = {{"label", "not null"}, {"id", "not 0"}}; + assertNotNull(savePoint.getLabel(), form.format(msgArgs[0])); try { + savePoint.getSavepointName(); - assertTrue(false, "Expecting Exception as trying to get SavePointname when we created savepoint without name"); + // Expecting Exception as trying to get SavePointname when we created savepoint without name + assertTrue(false, TestResource.getResource("R_shouldThrowException")); } catch (SQLException e) { } - assertTrue(savePoint.getSavepointId() != 0, "SavePoint should not be 0"); + assertTrue(savePoint.getSavepointId() != 0, form.format(msgArgs[1])); connection.rollback(); } @@ -97,7 +106,8 @@ public void testSavePointIsNamed() throws SQLException { SQLServerSavepoint savePoint = (SQLServerSavepoint) connection.setSavepoint(null); - assertFalse(savePoint.isNamed(), "SQLServerSavepoint.isNamed should be false as savePoint is created without name"); + // SQLServerSavepoint.isNamed should be false as savePoint is created without name" + assertFalse(savePoint.isNamed(), TestResource.getResource("R_shouldThrowException")); connection.rollback(); } @@ -115,7 +125,8 @@ public void testSavePointWithAutoCommit() throws SQLException { try { connection.setSavepoint(null); - assertTrue(false, "Expecting Exception as can not set SetPoint when AutoCommit mode is set to true."); + // Expecting Exception as can not set SetPoint when AutoCommit mode is set to true + assertTrue(false, TestResource.getResource("R_shouldThrowException")); } catch (SQLException e) { } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/lobs/lobsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/lobs/lobsTest.java index 232eb49b7..2753a86a8 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/lobs/lobsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/lobs/lobsTest.java @@ -40,6 +40,8 @@ import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; +import com.microsoft.sqlserver.jdbc.TestResource; + import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBCoercion; @@ -183,8 +185,8 @@ else if (lobClass == Blob.class) // Case 1: Invalid length value is passed as LOB length if (streamLength < 0 || streamLength == Long.MAX_VALUE) { // Applies to all LOB types ("The length {0} is not valid} - assertTrue(e.getMessage().startsWith("The length"), "Unexpected message thrown : " + e.getMessage()); - assertTrue(e.getMessage().endsWith("is not valid."), "Unexpected message thrown : " + e.getMessage()); + assertTrue(e.getMessage().startsWith("The length"), TestResource.getResource("R_unexpectedExceptionContent") + ": " + e.getMessage()); + assertTrue(e.getMessage().endsWith("is not valid."), TestResource.getResource("R_unexpectedExceptionContent") + ": " + e.getMessage()); verified = true; } @@ -203,12 +205,12 @@ else if (lobClass == Blob.class) } if (!verified) { // Odd CharacterStream length will throw this exception - if (!e.getMessage().contains("The stream value is not the specified length. The specified length was")) + if (!e.getMessage().contains(TestResource.getResource("R_badStreamLength"))) { if (lobClass == DBCharacterStream.class || lobClass == DBBinaryStream.class) - assertTrue(e.getSQLState() != null, "SQLState should not be null"); - assertTrue(e.getMessage().contains("An error occurred while reading the value from the stream object. Error:")); + assertTrue(e.getSQLState() != null, TestResource.getResource("R_SQLStateNull")); + assertTrue(e.getMessage().contains(TestResource.getResource("R_streamReadError"))); } } @@ -258,14 +260,14 @@ private void testFreedBlobs(Class lobClass, try { stream = blob.getBinaryStream(); } catch (SQLException e) { - assertTrue(e.getMessage().contains("This Blob object has been freed.")); + assertTrue(e.getMessage().contains(TestResource.getResource("R_blobFreed"))); } } rs.close(); try { stream = blob.getBinaryStream(); } catch (SQLException e) { - assertTrue(e.getMessage().contains("This Blob object has been freed.")); + assertTrue(e.getMessage().contains(TestResource.getResource("R_blobFreed"))); } } catch (Exception e) { @@ -308,7 +310,7 @@ private void testMultipleClose(Class streamClass) throws Exception { continue; Object stream = rs.getXXX(i + 1, streamClass); if (stream == null) { - assertEquals(stream, rs.getObject(i + 1), "Stream is null when data is not"); + assertEquals(stream, rs.getObject(i + 1), TestResource.getResource("R_streamNull")); } else { // close the stream twice @@ -652,7 +654,7 @@ else if (lob instanceof Clob) ps.setClob(index, (Clob) lob); else ps.setBlob(index, (Blob) lob); - assertEquals(ps.executeUpdate(), 1, "ExecuteUpdate did not return the correct updateCount"); + assertEquals(ps.executeUpdate(), 1, TestResource.getResource("R_incorrectUpdateCount")); } private void updateResultSet(ResultSet rs, @@ -729,4 +731,4 @@ private static void dropTables(DBTable table) throws SQLException { stmt.executeUpdate("if object_id('" + table.getEscapedTableName() + "','U') is not null" + " drop table " + table.getEscapedTableName()); } -} \ No newline at end of file +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java index 38c16ce95..1297446ca 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java @@ -28,6 +28,7 @@ import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBConnection; @@ -140,7 +141,7 @@ public void Repro47239() throws SQLException { log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test interleaved inserts and warnings"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_testInterleaved")); expectedUpdateCounts = new int[] {-3, 1, 1, 1}; stmt.addBatch(error); @@ -160,7 +161,7 @@ public void Repro47239() throws SQLException { log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test error followed by inserts"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_errorFollowInserts")); // 50280 expectedUpdateCounts = new int[] {1, -3}; stmt.addBatch(insertStmt); @@ -177,7 +178,7 @@ public void Repro47239() throws SQLException { log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test insert followed by non-fatal error (50280)"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_errorFollow50280")); // Test "soft" errors conn.setAutoCommit(false); @@ -187,12 +188,13 @@ public void Repro47239() throws SQLException { stmt.addBatch(insertStmt); try { stmt.executeBatch(); - assertEquals(true, false, "Soft error test: executeBatch unexpectedly succeeded"); + // Soft error test: executeBatch unexpectedly succeeded + assertEquals(true, false, TestResource.getResource("R_shouldThrowException")); } catch (BatchUpdateException bue) { - assertEquals("A result set was generated for update.", bue.getMessage(), "Soft error test: wrong error message in BatchUpdateException"); + assertEquals("A result set was generated for update.", bue.getMessage(), TestResource.getResource("R_unexpectedExceptionContent")); assertEquals(Arrays.equals(bue.getUpdateCounts(), new int[] {-3, 1, -3, 1}), true, - "Soft error test: wrong update counts in BatchUpdateException"); + TestResource.getResource("R_incorrectUpdateCount")); } conn.rollback(); @@ -205,12 +207,12 @@ public void Repro47239() throws SQLException { stmt.executeBatch(); } catch (BatchUpdateException bue) { - assertThat(bue.getMessage(), containsString("Syntax error converting date")); + assertThat(bue.getMessage(), containsString(TestResource.getResource("R_syntaxErrorDateConvert"))); // CTestLog.CompareStartsWith(bue.getMessage(), "Syntax error converting date", "Transaction rollback with conversion error threw wrong // BatchUpdateException"); } catch (SQLException e) { - assertThat(e.getMessage(), containsString("Conversion failed when converting date")); + assertThat(e.getMessage(), containsString(TestResource.getResource("R_dateConvertError"))); // CTestLog.CompareStartsWith(e.getMessage(), "Conversion failed when converting date", "Transaction rollback with conversion error threw // wrong SQLException"); } @@ -237,19 +239,22 @@ public void Repro47239() throws SQLException { stmt.addBatch(insertStmt); try { stmt.executeBatch(); - assertEquals(false, true, "Test fatal errors batch execution succeeded (should have failed)"); + // Test fatal errors batch execution succeeded (should have failed) + assertEquals(false, true, TestResource.getResource("R_shouldThrowException")); } catch (BatchUpdateException bue) { - assertEquals(false, true, "Test fatal errors returned BatchUpdateException rather than SQLException"); + // Test fatal errors returned BatchUpdateException rather than SQLException + assertEquals(false, true, TestResource.getResource("R_unexpectedException") + bue.getMessage()); + } catch (SQLException e) { actualExceptionText = e.getMessage(); if (actualExceptionText.endsWith("reset")) { - assertTrue(actualExceptionText.equalsIgnoreCase("Connection reset"), "Test fatal errors"); + assertTrue(actualExceptionText.equalsIgnoreCase("Connection reset"), TestResource.getResource("R_unexpectedExceptionContent") + ": " + actualExceptionText); } else { - assertTrue(actualExceptionText.equalsIgnoreCase("raiserror level 20"), "Test fatal errors"); + assertTrue(actualExceptionText.equalsIgnoreCase("raiserror level 20"), TestResource.getResource("R_unexpectedExceptionContent") + ": " + actualExceptionText); } } } @@ -272,7 +277,7 @@ public void Repro47239() throws SQLException { @DisplayName("Regression test for using 'large' methods") public void Repro47239large() throws Exception { - assumeTrue("JDBC42".equals(Utils.getConfiguredProperty("JDBC_Version")), "Aborting test case as JDBC version is not compatible. "); + assumeTrue("JDBC42".equals(Utils.getConfiguredProperty("JDBC_Version")), TestResource.getResource("R_incompatJDBC")); // the DBConnection for detecting whether the server is SQL Azure or SQL Server. con = DriverManager.getConnection(connectionString); final String warning; @@ -344,7 +349,7 @@ public void Repro47239large() throws Exception { log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test interleaved inserts and warnings"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_testInterleaved")); expectedUpdateCounts = new long[] {-3, 1, 1, 1}; stmt.addBatch(error); @@ -364,7 +369,7 @@ public void Repro47239large() throws Exception { log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test error followed by inserts"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_errorFollowInserts")); // 50280 expectedUpdateCounts = new long[] {1, -3}; @@ -382,7 +387,7 @@ public void Repro47239large() throws Exception { log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test insert followed by non-fatal error (50280)"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_errorFollow50280")); // Test "soft" errors conn.setAutoCommit(false); @@ -392,12 +397,15 @@ public void Repro47239large() throws Exception { stmt.addBatch(insertStmt); try { stmt.executeLargeBatch(); - assertEquals(false, true, "Soft error test: executeLargeBatch unexpectedly succeeded"); + // Soft error test: executeLargeBatch unexpectedly succeeded + assertEquals(false, true, TestResource.getResource("R_shouldThrowException")); } catch (BatchUpdateException bue) { - assertEquals("A result set was generated for update.", bue.getMessage(), "Soft error test: wrong error message in BatchUpdateException"); + // Soft error test: wrong error message in BatchUpdateException + assertEquals("A result set was generated for update.", bue.getMessage(), TestResource.getResource("R_unexpectedExceptionContent")); + // Soft error test: wrong update counts in BatchUpdateException assertEquals(Arrays.equals(bue.getLargeUpdateCounts(), new long[] {-3, 1, -3, 1}), true, - "Soft error test: wrong update counts in BatchUpdateException"); + TestResource.getResource("R_incorrectUpdateCount")); } conn.rollback(); @@ -410,10 +418,10 @@ public void Repro47239large() throws Exception { stmt.executeLargeBatch(); } catch (BatchUpdateException bue) { - assertThat(bue.getMessage(), containsString("Syntax error converting date")); + assertThat(bue.getMessage(), containsString(TestResource.getResource("R_syntaxErrorDateConvert"))); } catch (SQLException e) { - assertThat(e.getMessage(), containsString("Conversion failed when converting date")); + assertThat(e.getMessage(), containsString(TestResource.getResource("R_dateConvertError"))); } conn.setAutoCommit(true); @@ -437,19 +445,21 @@ public void Repro47239large() throws Exception { stmt.addBatch(insertStmt); try { stmt.executeLargeBatch(); - assertEquals(false, true, "Test fatal errors batch execution succeeded (should have failed)"); + // Test fatal errors batch execution succeeded (should have failed) + assertEquals(false, true, TestResource.getResource("R_shouldThrowException")); } catch (BatchUpdateException bue) { - assertEquals(false, true, "Test fatal errors returned BatchUpdateException rather than SQLException"); + // Test fatal errors returned BatchUpdateException rather than SQLException + assertEquals(false, true, TestResource.getResource("R_unexpectedException") + bue.getMessage()); } catch (SQLException e) { actualExceptionText = e.getMessage(); if (actualExceptionText.endsWith("reset")) { - assertTrue(actualExceptionText.equalsIgnoreCase("Connection reset"), "Test fatal errors"); + assertTrue(actualExceptionText.equalsIgnoreCase("Connection reset"), TestResource.getResource("R_unexpectedExceptionContent") + ": " + actualExceptionText); } else { - assertTrue(actualExceptionText.equalsIgnoreCase("raiserror level 20"), "Test fatal errors"); + assertTrue(actualExceptionText.equalsIgnoreCase("raiserror level 20"), TestResource.getResource("R_unexpectedExceptionContent") + ": " + actualExceptionText); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java index 85eb4a91b..c9de04a67 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java @@ -27,6 +27,7 @@ import org.opentest4j.TestAbortedException; import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBConnection; import com.microsoft.sqlserver.testframework.Utils; @@ -77,7 +78,7 @@ public void testAddBatch1() { int[] updateCount = pstmt.executeBatch(); int updateCountlen = updateCount.length; - assertTrue(updateCountlen == 3, "addBatch does not add the SQL Statements to Batch ,call to addBatch failed"); + assertTrue(updateCountlen == 3, TestResource.getResource("R_addBatchFailed") + ": " + TestResource.getResource("R_incorrectUpdateCount")); String sPrepStmt1 = "select count(*) from ctstable2 where TYPE_ID=?"; @@ -96,18 +97,18 @@ public void testAddBatch1() { for (int j = 0; j < updateCount.length; j++) { if (updateCount[j] != retValue[j] && updateCount[j] != Statement.SUCCESS_NO_INFO) { - fail("affected row count does not match with the updateCount value, Call to addBatch is Failed!"); + fail(TestResource.getResource("R_incorrectUpdateCount")); } } } catch (BatchUpdateException b) { - fail("BatchUpdateException : Call to addBatch is Failed!"); + fail(TestResource.getResource("R_addBatchFailed") + ": " + b.getMessage()); } catch (SQLException sqle) { - fail("Call to addBatch is Failed!"); + fail(TestResource.getResource("R_addBatchFailed") + ": " + sqle.getMessage()); } catch (Exception e) { - fail("Call to addBatch is Failed!"); + fail(TestResource.getResource("R_addBatchFailed") + ": " + e.getMessage()); } } @@ -118,7 +119,7 @@ public void testAddBatch1() { public void testExecuteBatch1() { int i = 0; int retValue[] = {0, 0, 0}; - int updCountLength = 0; + int updateCountlen = 0; try { String sPrepStmt = "update ctstable2 set PRICE=PRICE*20 where TYPE_ID=?"; @@ -133,9 +134,9 @@ public void testExecuteBatch1() { pstmt.addBatch(); int[] updateCount = pstmt.executeBatch(); - updCountLength = updateCount.length; + updateCountlen = updateCount.length; - assertTrue(updCountLength == 3, "executeBatch does not execute the Batch of SQL statements, Call to executeBatch is Failed!"); + assertTrue(updateCountlen == 3, TestResource.getResource("R_executeBatchFailed") + ": " + TestResource.getResource("R_incorrectUpdateCount")); String sPrepStmt1 = "select count(*) from ctstable2 where TYPE_ID=?"; @@ -152,18 +153,18 @@ public void testExecuteBatch1() { for (int j = 0; j < updateCount.length; j++) { if (updateCount[j] != retValue[j] && updateCount[j] != Statement.SUCCESS_NO_INFO) { - fail("executeBatch does not execute the Batch of SQL statements, Call to executeBatch is Failed!"); + fail(TestResource.getResource("R_executeBatchFailed") + ": " + TestResource.getResource("R_incorrectUpdateCount")); } } } catch (BatchUpdateException b) { - fail("BatchUpdateException : Call to executeBatch is Failed!"); + fail(TestResource.getResource("R_executeBatchFailed") + ": " + b.getMessage()); } catch (SQLException sqle) { - fail("Call to executeBatch is Failed!"); + fail(TestResource.getResource("R_executeBatchFailed") + ": " + sqle.getMessage()); } catch (Exception e) { - fail("Call to executeBatch is Failed!"); + fail(TestResource.getResource("R_executeBatchFailed") + ": " + e.getMessage()); } } @@ -192,7 +193,7 @@ private static void createTable() throws SQLException { @BeforeAll public static void testSetup() throws TestAbortedException, Exception { assumeTrue(13 <= new DBConnection(connectionString).getServerVersion(), - "Aborting test case as SQL Server version is not compatible with Always encrypted "); + TestResource.getResource("R_Incompat_SQLServerVersion")); connection = DriverManager.getConnection(connectionString + ";columnEncryptionSetting=Enabled;"); stmt = (SQLServerStatement) connection.createStatement(); dropTable(); @@ -225,4 +226,4 @@ public static void terminateVariation() throws SQLException { rs.close(); } } -} \ No newline at end of file +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchTriggerTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchTriggerTest.java index 1abcf471c..5115a1f7f 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchTriggerTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchTriggerTest.java @@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.fail; import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; @@ -37,7 +38,6 @@ public class BatchTriggerTest extends AbstractTest { static Connection connection = null; static String tableName = "triggerTable"; static String triggerName = "triggerTest"; - static String customErrorMessage = "Custom error message, you should see me. col1 should be higher than 10"; static String insertQuery = "insert into " + tableName + " (col1, col2, col3, col4) values (1, '22-08-2017 17:30:00.000', 'R4760', 31)"; /** @@ -52,10 +52,10 @@ public void statementTest() throws SQLException { stmt = connection.createStatement(); stmt.addBatch(insertQuery); stmt.executeBatch(); - fail("Trigger Exception not thrown"); + fail(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (Exception e) { - assertTrue(e.getMessage().equalsIgnoreCase(customErrorMessage)); + assertTrue(e.getMessage().equalsIgnoreCase(TestResource.getResource("R_customErrorMessage"))); } finally { @@ -77,11 +77,11 @@ public void preparedStatementTest() throws SQLException { pstmt = connection.prepareStatement(insertQuery); pstmt.addBatch(); pstmt.executeBatch(); - fail("Trigger Exception not thrown"); + fail(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (Exception e) { - assertTrue(e.getMessage().equalsIgnoreCase(customErrorMessage)); + assertTrue(e.getMessage().equalsIgnoreCase(TestResource.getResource("R_customErrorMessage"))); } finally { if (pstmt != null) { @@ -99,7 +99,7 @@ public void preparedStatementTest() throws SQLException { private static void createTrigger(String triggerName) throws SQLException { String sql = "create trigger " + triggerName + " on " + tableName + " for insert " + "as " + "begin " + "if (select col1 from " + tableName + ") > 10 " + "begin " + "return " + "end " - + "RAISERROR ('Custom error message, you should see me. col1 should be higher than 10', 16, 0) " + "rollback transaction " + "end"; + + "RAISERROR ('" + TestResource.getResource("R_customErrorMessage") + "', 16, 0) " + "rollback transaction " + "end"; stmt.execute(sql); } @@ -158,4 +158,4 @@ public static void terminateVariation() throws SQLException { } } -} \ No newline at end of file +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/CallableMixedTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/CallableMixedTest.java index e93f1c150..ef0321af3 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/CallableMixedTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/CallableMixedTest.java @@ -20,6 +20,7 @@ import org.junit.runner.RunWith; import static org.junit.jupiter.api.Assertions.assertEquals; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; @@ -66,21 +67,21 @@ public void datatypesTest() throws SQLException { ResultSet rs = callableStatement.executeQuery(); rs.next(); - assertEquals(rs.getInt(1), 0, "Received data not equal to setdata"); - assertEquals(callableStatement.getInt((int) 5), -5372, "Received data not equal to setdata"); + assertEquals(rs.getInt(1), 0, TestResource.getResource("R_setDataNotEqual")); + assertEquals(callableStatement.getInt((int) 5), -5372, TestResource.getResource("R_setDataNotEqual")); // do nothing and reexecute rs = callableStatement.executeQuery(); // get the param without getting the resultset rs = callableStatement.executeQuery(); - assertEquals(callableStatement.getInt((int) 1), -2147483648, "Received data not equal to setdata"); + assertEquals(callableStatement.getInt((int) 1), -2147483648, TestResource.getResource("R_setDataNotEqual")); rs = callableStatement.executeQuery(); rs.next(); - assertEquals(rs.getInt(1), 0, "Received data not equal to setdata"); - assertEquals(callableStatement.getInt((int) 1), -2147483648, "Received data not equal to setdata"); - assertEquals(callableStatement.getInt((int) 5), -5372, "Received data not equal to setdata"); + assertEquals(rs.getInt(1), 0, TestResource.getResource("R_setDataNotEqual")); + assertEquals(callableStatement.getInt((int) 1), -2147483648, TestResource.getResource("R_setDataNotEqual")); + assertEquals(callableStatement.getInt((int) 5), -5372, TestResource.getResource("R_setDataNotEqual")); rs = callableStatement.executeQuery(); rs.close(); } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/LimitEscapeTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/LimitEscapeTest.java index 0453b5df1..b801cc10f 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/LimitEscapeTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/LimitEscapeTest.java @@ -31,6 +31,7 @@ import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; @@ -123,7 +124,7 @@ public void verifyTranslation() throws Exception { method.setAccessible(true); Object str = method.invoke(innerInstance, inputSql); - assertEquals(str, outputSql, "Syntax tyranslation does not match for query: " + queryID); + assertEquals(str, outputSql, TestResource.getResource("R_syntaxMatchError") + ": " + queryID); } public void setverifyResult(boolean val) { @@ -142,7 +143,7 @@ void execute(Connection conn) throws Exception { catch (Exception e) { if (null != exceptionMsg) { // This query is to verify right exception is thrown for errors in syntax. - assertTrue(e.getMessage().equalsIgnoreCase(exceptionMsg), "Test fatal errors"); + assertTrue(e.getMessage().equalsIgnoreCase(exceptionMsg), TestResource.getResource("R_unexpectedExceptionContent") + e.getMessage()); // Exception message matched. Return as there is no result to verify. return; } @@ -155,28 +156,28 @@ void execute(Connection conn) throws Exception { } if (null == resultSet) { - assertEquals(false, true, "ResultSet is null"); + assertEquals(false, true, TestResource.getResource("R_resultsetNull")); } int rowCount = 0; while (resultSet.next()) { // The int and string columns should be retrieved in order, for example cannot run a query that retrieves col2 but not col1 - assertEquals(resultSet.getInt(1), idCols[rowCount], "ID value does not match for query: " + queryID + ", row: " + rowCount); + assertEquals(resultSet.getInt(1), idCols[rowCount], TestResource.getResource("R_valueNotMatch") + queryID + ", row: " + rowCount); for (int j = 0, colNumber = 1; null != intResultCols && j < intResultCols[rowCount].length; ++j) { String colName = "col" + colNumber; assertEquals(resultSet.getInt(colName), intResultCols[rowCount][j], - "Int value does not match for query: " + queryID + ", row: " + rowCount + ", column: " + colName); + TestResource.getResource("R_valueNotMatch") + queryID + ", row: " + rowCount + ", column: " + colName); colNumber++; } for (int j = 0, colNumber = 3; null != stringResultCols && j < stringResultCols[rowCount].length; ++j) { String colName = "col" + colNumber; assertEquals(resultSet.getString(colName), stringResultCols[rowCount][j], - "String value does not match for query: " + queryID + ", row: " + rowCount + ", column: " + colName); + TestResource.getResource("R_valueNotMatch") + queryID + ", row: " + rowCount + ", column: " + colName); colNumber++; } rowCount++; } - assertEquals(rowCount, rows, "Row Count does not match for query"); + assertEquals(rowCount, rows, TestResource.getResource("R_valueNotMatch") + "rowCount: " + rowCount + ", rows: " + rows); assertEquals(resultSet.getMetaData().getColumnCount(), columns, "Column Count does not match"); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/MergeTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/MergeTest.java index 41674e562..5f681796a 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/MergeTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/MergeTest.java @@ -21,6 +21,7 @@ import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBConnection; import com.microsoft.sqlserver.testframework.DBStatement; @@ -62,7 +63,7 @@ public void runTest() throws Exception { stmt.executeUpdate(setupTables); stmt.executeUpdate(mergeCmd2); int updateCount = stmt.getUpdateCount(); - assertEquals(updateCount, 3, "Received the wrong update count!"); + assertEquals(updateCount, 3, TestResource.getResource("R_incorrectUpdateCount")); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/NamedParamMultiPartTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/NamedParamMultiPartTest.java index 2ee818313..62997ffcc 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/NamedParamMultiPartTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/NamedParamMultiPartTest.java @@ -23,6 +23,7 @@ import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; @@ -61,7 +62,7 @@ public void update1() throws Exception { cs.registerOutParameter("p_out", Types.VARCHAR); cs.executeUpdate(); String data = cs.getString("p_out"); - assertEquals(data, dataPut, "Received data not equal to setdata"); + assertEquals(data, dataPut, TestResource.getResource("R_setDataNotEqual")); } } @@ -76,7 +77,7 @@ public void update2() throws Exception { cs.registerOutParameter("p_out", Types.VARCHAR); cs.executeUpdate(); Object data = cs.getObject("p_out"); - assertEquals(data, dataPut, "Received data not equal to setdata"); + assertEquals(data, dataPut, TestResource.getResource("R_setDataNotEqual")); } } @@ -93,7 +94,7 @@ public void update3() throws Exception { cs.registerOutParameter("p_out", Types.VARCHAR); cs.executeUpdate(); Object data = cs.getObject("p_out"); - assertEquals(data, dataPut, "Received data not equal to setdata"); + assertEquals(data, dataPut, TestResource.getResource("R_setDataNotEqual")); } } @@ -108,7 +109,7 @@ public void update4() throws Exception { cs.registerOutParameter("p_out", Types.VARCHAR); cs.executeUpdate(); Object data = cs.getObject("p_out"); - assertEquals(data, dataPut, "Received data not equal to setdata"); + assertEquals(data, dataPut, TestResource.getResource("R_setDataNotEqual")); } } @@ -123,7 +124,7 @@ public void update5() throws Exception { cs.registerOutParameter("p_out", Types.VARCHAR); cs.executeUpdate(); Object data = cs.getObject("p_out"); - assertEquals(data, dataPut, "Received data not equal to setdata"); + assertEquals(data, dataPut, TestResource.getResource("R_setDataNotEqual")); } } @@ -139,7 +140,7 @@ public void update6() throws Exception { cs.registerOutParameter("p_out", Types.VARCHAR); cs.executeUpdate(); Object data = cs.getObject("p_out"); - assertEquals(data, dataPut, "Received data not equal to setdata"); + assertEquals(data, dataPut, TestResource.getResource("R_setDataNotEqual")); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PQImpsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PQImpsTest.java index 55acb2ca8..210dd264c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PQImpsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PQImpsTest.java @@ -27,6 +27,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerConnection; import com.microsoft.sqlserver.jdbc.SQLServerParameterMetaData; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; @@ -227,7 +228,7 @@ private static void checkNumericMetaData() throws SQLException { ParameterMetaData pmd = pstmt.getParameterMetaData(); - assertEquals(pmd.getParameterCount(), 15, "Not all parameters are recognized by driver."); + assertEquals(pmd.getParameterCount(), 15, TestResource.getResource("R_paramNotRecognized")); compareParameterMetaData(pmd, 1, "java.math.BigDecimal", 3, "decimal", 18, 0); compareParameterMetaData(pmd, 2, "java.math.BigDecimal", 3, "decimal", 10, 5); @@ -250,7 +251,7 @@ private static void checkCharMetaData(int expectedParameterCount) throws SQLExce ParameterMetaData pmd = pstmt.getParameterMetaData(); - assertEquals(pmd.getParameterCount(), expectedParameterCount, "Not all parameters are recognized by driver."); + assertEquals(pmd.getParameterCount(), expectedParameterCount, TestResource.getResource("R_paramNotRecognized")); compareParameterMetaData(pmd, 1, "java.lang.String", 1, "char", 50, 0); compareParameterMetaData(pmd, 2, "java.lang.String", 12, "varchar", 20, 0); @@ -267,7 +268,7 @@ private static void checkBinaryMetaData() throws SQLException { ParameterMetaData pmd = pstmt.getParameterMetaData(); - assertEquals(pmd.getParameterCount(), 2, "Not all parameters are recognized by driver."); + assertEquals(pmd.getParameterCount(), 2, TestResource.getResource("R_paramNotRecognized")); compareParameterMetaData(pmd, 1, "[B", -2, "binary", 100, 0); compareParameterMetaData(pmd, 2, "[B", -3, "varbinary", 200, 0); @@ -276,7 +277,7 @@ private static void checkBinaryMetaData() throws SQLException { private static void checkDateAndTimeMetaData() throws SQLException { ParameterMetaData pmd = pstmt.getParameterMetaData(); - assertEquals(pmd.getParameterCount(), 9, "Not all parameters are recognized by driver."); + assertEquals(pmd.getParameterCount(), 9, TestResource.getResource("R_paramNotRecognized")); compareParameterMetaData(pmd, 1, "java.sql.Date", 91, "date", 10, 0); compareParameterMetaData(pmd, 2, "java.sql.Timestamp", 93, "datetime", 23, 3); @@ -306,7 +307,7 @@ private static void compareParameterMetaData(ParameterMetaData pmd, } try { assertTrue(pmd.getParameterType(index) == expectedType, - "Parameter Type error:\n" + "expected: " + expectedType + " \n" + "actual: " + pmd.getParameterType(index)); + "getParameterType: " + TestResource.getResource("R_valueNotMatch") + expectedType + ", " + pmd.getParameterType(index)); } catch (SQLException e) { fail(e.toString()); @@ -314,14 +315,14 @@ private static void compareParameterMetaData(ParameterMetaData pmd, try { assertTrue(pmd.getParameterTypeName(index).equalsIgnoreCase(expectedTypeName), - "Parameter Type Name error:\n" + "expected: " + expectedTypeName + " \n" + "actual: " + pmd.getParameterTypeName(index)); + "getParameterTypeName: " + TestResource.getResource("R_valueNotMatch") + expectedTypeName + ", " + pmd.getParameterTypeName(index)); } catch (SQLException e) { fail(e.toString()); } try { assertTrue(pmd.getPrecision(index) == expectedPrecision, - "Parameter Prcision error:\n" + "expected: " + expectedPrecision + " \n" + "actual: " + pmd.getPrecision(index)); + "getPrecision: " + TestResource.getResource("R_valueNotMatch") + expectedPrecision + ", " + pmd.getPrecision(index)); } catch (SQLException e) { fail(e.toString()); @@ -329,7 +330,7 @@ private static void compareParameterMetaData(ParameterMetaData pmd, try { assertTrue(pmd.getScale(index) == expectedScale, - "Parameter Prcision error:\n" + "expected: " + expectedScale + " \n" + "actual: " + pmd.getScale(index)); + "getScale: " + TestResource.getResource("R_valueNotMatch") + expectedScale + ", " + pmd.getScale(index)); } catch (SQLException e) { fail(e.toString()); @@ -744,7 +745,7 @@ public void testSubquery() throws SQLException { try { pmd = pstmt.getParameterMetaData(); - assertEquals(pmd.getParameterCount(), 3, "Not all parameters are recognized by driver."); + assertEquals(pmd.getParameterCount(), 3, TestResource.getResource("R_paramNotRecognized")); } catch (Exception e) { fail(e.toString()); @@ -777,7 +778,7 @@ public void testJoin() throws SQLException { try { pmd = pstmt.getParameterMetaData(); - assertEquals(pmd.getParameterCount(), 2, "Not all parameters are recognized by driver."); + assertEquals(pmd.getParameterCount(), 2, TestResource.getResource("R_paramNotRecognized")); } catch (Exception e) { fail(e.toString()); @@ -810,7 +811,7 @@ public void testMerge() throws SQLException { try { pmd = pstmt.getParameterMetaData(); - assertEquals(pmd.getParameterCount(), 2, "Not all parameters are recognized by driver."); + assertEquals(pmd.getParameterCount(), 2, TestResource.getResource("R_paramNotRecognized")); } catch (Exception e) { fail(e.toString()); @@ -863,7 +864,7 @@ private static void testInsertMultipleTypes() throws SQLException { ParameterMetaData pmd = null; try { pmd = pstmt.getParameterMetaData(); - assertEquals(pmd.getParameterCount(), 30, "Not all parameters are recognized by driver."); + assertEquals(pmd.getParameterCount(), 30, TestResource.getResource("R_paramNotRecognized")); } catch (Exception e) { fail(e.toString()); @@ -921,7 +922,7 @@ public void testNoParameter() throws SQLException { ParameterMetaData pmd = null; try { pmd = pstmt.getParameterMetaData(); - assertEquals(pmd.getParameterCount(), 0, "Not all parameters are recognized by driver."); + assertEquals(pmd.getParameterCount(), 0, TestResource.getResource("R_paramNotRecognized")); } catch (Exception e) { fail(e.toString()); @@ -955,7 +956,7 @@ private static void testMixedWithHardcodedValues() throws SQLException { try { pmd = pstmt.getParameterMetaData(); - assertEquals(pmd.getParameterCount(), 21, "Not all parameters are recognized by driver."); + assertEquals(pmd.getParameterCount(), 21, TestResource.getResource("R_paramNotRecognized")); } catch (Exception e) { fail(e.toString()); @@ -1004,7 +1005,7 @@ public void testOrderBy() throws SQLException { try { pmd = pstmt.getParameterMetaData(); - assertEquals(pmd.getParameterCount(), 4, "Not all parameters are recognized by driver."); + assertEquals(pmd.getParameterCount(), 4, TestResource.getResource("R_paramNotRecognized")); } catch (Exception e) { fail(e.toString()); @@ -1034,7 +1035,7 @@ private void testGroupBy() throws SQLException { try { pmd = pstmt.getParameterMetaData(); - assertEquals(pmd.getParameterCount(), 4, "Not all parameters are recognized by driver."); + assertEquals(pmd.getParameterCount(), 4, TestResource.getResource("R_paramNotRecognized")); } catch (Exception e) { fail(e.toString()); @@ -1063,7 +1064,7 @@ public void testLower() throws SQLException { try { pmd = pstmt.getParameterMetaData(); - assertEquals(pmd.getParameterCount(), 4, "Not all parameters are recognized by driver."); + assertEquals(pmd.getParameterCount(), 4, TestResource.getResource("R_paramNotRecognized")); } catch (Exception e) { fail(e.toString()); @@ -1091,7 +1092,7 @@ public void testPower() throws SQLException { try { pmd = pstmt.getParameterMetaData(); - assertEquals(pmd.getParameterCount(), 4, "Not all parameters are recognized by driver."); + assertEquals(pmd.getParameterCount(), 4, TestResource.getResource("R_paramNotRecognized")); } catch (Exception e) { fail(e.toString()); @@ -1126,7 +1127,7 @@ public void testAllInOneQuery() throws SQLException { try { pmd = pstmt.getParameterMetaData(); - assertEquals(pmd.getParameterCount(), 3, "Not all parameters are recognized by driver."); + assertEquals(pmd.getParameterCount(), 3, TestResource.getResource("R_paramNotRecognized")); } catch (Exception e) { fail(e.toString()); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PoolableTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PoolableTest.java index 7ce0773de..72fd663d5 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PoolableTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PoolableTest.java @@ -26,6 +26,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerCallableStatement; import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; @@ -49,17 +50,17 @@ public void poolableTest() throws SQLException, ClassNotFoundException { try { // First get the default values boolean isPoolable = ((SQLServerStatement) statement).isPoolable(); - assertEquals(isPoolable, false, "SQLServerStatement should not be Poolable by default"); + assertEquals(isPoolable, false, "SQLServerStatement: " + TestResource.getResource("R_incorrectDefault")); try (PreparedStatement prepStmt = connection.prepareStatement("select 1")) { isPoolable = ((SQLServerPreparedStatement) prepStmt).isPoolable(); - assertEquals(isPoolable, true, "SQLServerPreparedStatement should be Poolable by default"); + assertEquals(isPoolable, true, "SQLServerPreparedStatement: " + TestResource.getResource("R_incorrectDefault")); } try (CallableStatement callableStatement = connection.prepareCall("{ ? = CALL " + "ProcName" + " (?, ?, ?, ?) }");) { isPoolable = ((SQLServerCallableStatement) callableStatement).isPoolable(); - assertEquals(isPoolable, true, "SQLServerCallableStatement should be Poolable by default"); + assertEquals(isPoolable, true, "SQLServerCallableStatement: " + TestResource.getResource("R_incorrectDefault")); // Now do couple of sets and gets @@ -71,8 +72,10 @@ public void poolableTest() throws SQLException, ClassNotFoundException { assertEquals(((SQLServerStatement) statement).isPoolable(), true, "set did not work"); } catch (UnsupportedOperationException e) { - assertEquals(System.getProperty("java.specification.version"), "1.5", "PoolableTest should be supported in anything other than 1.5"); - assertEquals(e.getMessage(), "This operation is not supported.", "Wrong exception message"); + // PoolableTest should be supported in anything other than 1.5 + assertEquals(System.getProperty("java.specification.version"), "1.5", "PoolableTest " + TestResource.getResource("R_shouldBeSupported")); + assertEquals(e.getMessage(), TestResource.getResource("R_operationNotSupported")); + assertEquals(e.getMessage(), TestResource.getResource("R_unexpectedExceptionContent")); } } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java index f64e6d570..9e886d441 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java @@ -33,6 +33,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerConnection; import com.microsoft.sqlserver.jdbc.SQLServerDataSource; import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; @RunWith(JUnitPlatform.class) @@ -286,7 +287,7 @@ public void testStatementPooling() throws SQLException { } try { System.out.println(outer.getPreparedStatementHandle()); - fail("Error for invalid use of getPreparedStatementHandle() after statement close expected."); + fail(TestResource.getResource("R_invalidGetPreparedStatementHandle")); } catch(Exception e) { // Good! @@ -568,4 +569,4 @@ public void testStatementPoolingPreparedStatementExecAndUnprepareConfig() throws assertSame(0, con.getDiscardedServerPreparedStatementCount()); } } -} \ No newline at end of file +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/RegressionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/RegressionTest.java index 2eec0cd70..1efa1e25a 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/RegressionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/RegressionTest.java @@ -27,6 +27,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerConnection; import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBConnection; import com.microsoft.sqlserver.testframework.Utils; @@ -83,10 +84,10 @@ public void testServerCursorPStmt() throws SQLException { // should return 1 row rs = pstmt.executeQuery(); rs.last(); - assertEquals(rs.getRow(), numRowsInResult, "getRow mismatch"); + assertEquals(rs.getRow(), numRowsInResult, TestResource.getResource("R_valueNotMatch") + rs.getRow() + ", " + numRowsInResult); rs.beforeFirst(); while (rs.next()) { - assertEquals(rs.getString(1), col3Value, "Value mismatch"); + assertEquals(rs.getString(1), col3Value, TestResource.getResource("R_valueNotMatch") + rs.getString(1) + ", " + col3Value); } if (null != stmt) @@ -120,7 +121,7 @@ public void testSelectIntoUpdateCount() throws SQLException { PreparedStatement ps = con.prepareStatement("SELECT * INTO #TMPTABLE FROM " + tableName + " WHERE col1 <= ?"); ps.setInt(1, numRowsToCopy); int updateCount = ps.executeUpdate(); - assertEquals(numRowsToCopy, updateCount, "Incorrect update count"); + assertEquals(numRowsToCopy, updateCount, TestResource.getResource("R_incorrectUpdateCount")); if (null != stmt) stmt.close(); @@ -136,7 +137,7 @@ public void testSelectIntoUpdateCount() throws SQLException { */ @Test public void testUpdateQuery() throws SQLException { - assumeTrue("JDBC41".equals(Utils.getConfiguredProperty("JDBC_Version")), "Aborting test case as JDBC version is not compatible. "); + assumeTrue("JDBC41".equals(Utils.getConfiguredProperty("JDBC_Version")), TestResource.getResource("R_incompatJDBC")); SQLServerConnection con = (SQLServerConnection) DriverManager.getConnection(connectionString); String sql; @@ -197,7 +198,7 @@ public void testUpdateQuery() throws SQLException { */ @Test public void testXmlQuery() throws SQLException { - assumeTrue("JDBC41".equals(Utils.getConfiguredProperty("JDBC_Version")), "Aborting test case as JDBC version is not compatible. "); + assumeTrue("JDBC41".equals(Utils.getConfiguredProperty("JDBC_Version")), TestResource.getResource("R_incompatJDBC")); Connection connection = DriverManager.getConnection(connectionString); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementCancellationTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementCancellationTest.java index 0a2abb2c1..224414a9f 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementCancellationTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementCancellationTest.java @@ -18,6 +18,7 @@ import org.junit.runner.RunWith; import com.microsoft.sqlserver.jdbc.SQLServerDataSource; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; @RunWith(JUnitPlatform.class) @@ -59,7 +60,8 @@ public void run() { stmt.execute("WAITFOR DELAY '00:00:" + (DELAY_WAIT_MILLISECONDS / 1000) + "'"); } catch (SQLException e) { - assertTrue(e.getMessage().startsWith("The query was canceled"), "Unexpected error message."); + // The query was canceled"), "Unexpected error message + assertTrue(e.getMessage().startsWith(TestResource.getResource("R_queryCancelled")), TestResource.getResource("R_unexpectedExceptionContent")); } } } @@ -67,9 +69,9 @@ public void run() { finally { timeEnd = System.currentTimeMillis(); long timeDifference = timeEnd - timeStart; - assertTrue(timeDifference >= CANCEL_WAIT_MILLISECONDS, "Cancellation failed."); - assertTrue(timeDifference < DELAY_WAIT_MILLISECONDS, "Cancellation failed."); - assertTrue((timeDifference - CANCEL_WAIT_MILLISECONDS) < 1000, "Cancellation failed."); + assertTrue(timeDifference >= CANCEL_WAIT_MILLISECONDS, TestResource.getResource("R_cancellationFailed")); + assertTrue(timeDifference < DELAY_WAIT_MILLISECONDS, TestResource.getResource("R_cancellationFailed")); + assertTrue((timeDifference - CANCEL_WAIT_MILLISECONDS) < 1000, TestResource.getResource("R_cancellationFailed")); } } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java index 418068d90..2b322c47b 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java @@ -48,6 +48,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerResultSet; import com.microsoft.sqlserver.jdbc.SQLServerResultSetMetaData; import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBConnection; @@ -121,7 +122,8 @@ public void testCancelBeforeExecute() throws Exception { while (rs.next()) ++numSelectedRows; - assertEquals(NUM_TABLE_ROWS, numSelectedRows, "Wrong number of rows returned"); + // Wrong number of rows returned + assertEquals(NUM_TABLE_ROWS, numSelectedRows, TestResource.getResource("R_valueNotMatch")); stmt.close(); con.close(); } @@ -154,7 +156,7 @@ public void testErrorInRequest() throws Exception { assertEquals( "The stream value is not the specified length. The specified length was " + (TEST_STRING.length() - 1) + ", the actual length is " + TEST_STRING.length() + ".", - e.getMessage(), "Unexpected exception executing batch update with bad value."); + e.getMessage(), TestResource.getResource("R_unexpectedException")); } // Successfully closing the PreparedStatement is verification enough that the connection is @@ -184,14 +186,15 @@ public void testQueryTimeout() throws Exception { try { ps.execute(); - assertEquals(false, true, "Execution did not timeout"); + assertEquals(false, true, TestResource.getResource("R_executionNotTimeout")); } catch (SQLException e) { - assertTrue("The query has timed out.".equalsIgnoreCase(e.getMessage()), "Unexpected exception on 1st execution"); + assertTrue(TestResource.getResource("R_queryTimedOut").equalsIgnoreCase(e.getMessage()), TestResource.getResource("R_unexpectedException")); + assertTrue("The query has timed out.".equalsIgnoreCase(e.getMessage()), TestResource.getResource("R_unexpectedException")); } elapsedMillis += System.currentTimeMillis(); if (elapsedMillis >= 3000) { - assertEquals(2000, (int) elapsedMillis, "1st execution took too long"); + assertEquals(2000, (int) elapsedMillis, TestResource.getResource("R_executionTooLong")); } // Second execution: @@ -205,7 +208,7 @@ public void testQueryTimeout() throws Exception { // Oddly enough, the server's idea of 7 seconds is actually slightly less than // 7000 milliseconds by our clock (!) so we have to allow some slack here. if (elapsedMillis < 6500) { - assertEquals(6500, (int) elapsedMillis, "2nd execution didn't take long enough."); + assertEquals(6500, (int) elapsedMillis, TestResource.getResource("R_executionNotLong")); } ps.close(); @@ -222,7 +225,7 @@ public void testQueryTimeout() throws Exception { */ @Test public void testCancelLongResponse() throws Exception { - assumeTrue("JDBC42".equals(Utils.getConfiguredProperty("JDBC_Version")), "Aborting test case as JDBC version is not compatible. "); + assumeTrue("JDBC42".equals(Utils.getConfiguredProperty("JDBC_Version")), TestResource.getResource("R_incompatJDBC")); Connection con = DriverManager.getConnection(connectionString); Statement stmt = con.createStatement(SQLServerResultSet.TYPE_SS_DIRECT_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); ((SQLServerStatement) stmt).setResponseBuffering("adaptive"); @@ -233,7 +236,7 @@ public void testCancelLongResponse() throws Exception { } catch (Exception e) { - throw new SQLException("testCancelLongResponse threw exception: ", e); + throw new SQLException(TestResource.getResource("R_unexpectedException") + ": ", e); } @@ -245,7 +248,8 @@ public void testCancelLongResponse() throws Exception { ; // Verify that MIN_TABLE_ROWS rows were returned - assertEquals(MIN_TABLE_ROWS, numSelectedRows, "Wrong number of rows returned in first scan"); + // Wrong number of rows returned in first scan + assertEquals(MIN_TABLE_ROWS, numSelectedRows, TestResource.getResource("R_valueNotMatch")); // Cancel the statement and verify that the ResultSet // does NOT return all the remaining rows. @@ -255,16 +259,16 @@ public void testCancelLongResponse() throws Exception { while (rs.next()) ++numSelectedRows; - assertEquals(false, true, "Expected exception not thrown from ResultSet.next()"); + assertEquals(false, true, TestResource.getResource("R_expectedExceptionNotThrown")); } catch (SQLException e) { - assertEquals("The query was canceled.", e.getMessage(), "Unexpected exception from ResultSet.next()"); + assertEquals(TestResource.getResource("R_queryCancelled"), TestResource.getResource("R_unexpectedException")); } assertEquals(false, NUM_TABLE_ROWS * NUM_TABLE_ROWS == numSelectedRows, "All rows returned after cancel"); rs.close(); - assertEquals(stmt.isClosed(), true, "testCancelLongResponse: statement should be closed since resultset is closed."); + assertEquals(stmt.isClosed(), true, TestResource.getResource("R_statementShouldBeClosed")); con.close(); } @@ -363,10 +367,10 @@ public void testCancelBlockedResponse() throws Exception { ++numSelectedRows; log.fine("numSelectedRows: " + numSelectedRows); - assertEquals(false, true, "Expected exception not thrown from ResultSet.next()"); + assertEquals(false, true, TestResource.getResource("R_expectedExceptionNotThrown")); } catch (SQLException e) { - assertTrue("The query was canceled.".equalsIgnoreCase(e.getMessage()), "Unexpected exception from ResultSet.next()"); + assertTrue(TestResource.getResource("R_queryCancelled").equalsIgnoreCase(e.getMessage()), TestResource.getResource("R_unexpectedException")); } elapsedMillis += System.currentTimeMillis(); @@ -375,14 +379,14 @@ public void testCancelBlockedResponse() throws Exception { // Note that we may actually get fewer rows than the number of rows before the blocked row // if SQL Server is a little slow in returning rows to us. if (numSelectedRows >= NUM_TABLE_ROWS - MIN_TABLE_ROWS) { - assertEquals(NUM_TABLE_ROWS - MIN_TABLE_ROWS, numSelectedRows, "Wrong number of rows returned"); + assertEquals(NUM_TABLE_ROWS - MIN_TABLE_ROWS, numSelectedRows, TestResource.getResource("R_valueNotMatch")); } // If we were able to iterate through all of the expected // rows without blocking, then something went wrong with our // efforts to block execution. if (elapsedMillis < 2500) { - assertEquals(2500, (int) elapsedMillis, "Statement executed too quickly."); + assertEquals(2500, (int) elapsedMillis, TestResource.getResource("R_executionNotLong")); } rs.close(); @@ -476,10 +480,10 @@ public void testCancelBlockedResponsePS() throws Exception { while (rs.next()) ++numSelectedRows; - assertEquals(false, true, "Expected exception not thrown from ResultSet.next()"); + assertEquals(false, true, TestResource.getResource("R_expectedExceptionNotThrown")); } catch (SQLException e) { - assertTrue("The query was canceled.".contains(e.getMessage()), "Unexpected exception from ResultSet.next()"); + assertTrue(TestResource.getResource("R_queryCancelled").contains(e.getMessage()), TestResource.getResource("R_unexpectedException")); } elapsedMillis += System.currentTimeMillis(); @@ -488,14 +492,14 @@ public void testCancelBlockedResponsePS() throws Exception { // Note that we may actually get fewer rows than the number of rows before the blocked row // if SQL Server is a little slow in returning rows to us. if (numSelectedRows >= NUM_TABLE_ROWS - MIN_TABLE_ROWS) { - assertEquals(NUM_TABLE_ROWS - MIN_TABLE_ROWS, numSelectedRows, "Wrong number of rows returned"); + assertEquals(NUM_TABLE_ROWS - MIN_TABLE_ROWS, numSelectedRows, TestResource.getResource("R_valueNotMatch")); } // If we were able to iterate through all of the expected // rows without blocking, then something went wrong with our // efforts to block execution. if (elapsedMillis < 2500) { - assertEquals(2500, (int) elapsedMillis, "Statement executed too quickly."); + assertEquals(2500, (int) elapsedMillis, TestResource.getResource("R_executionNotLong")); } rs.close(); @@ -587,7 +591,7 @@ public void testCancelBlockedCursoredResponse() throws Exception { while (numSelectedRows < MIN_TABLE_ROWS && rs.next()) ++numSelectedRows; - assertEquals(MIN_TABLE_ROWS, numSelectedRows, "Too few rows returned initially."); + assertEquals(MIN_TABLE_ROWS, numSelectedRows, TestResource.getResource("R_valueNotMatch")); // Now, try to grab the remaining rows from the result set. At some point the call // to ResultSet.next() should block until the statement is cancelled from the other @@ -596,17 +600,17 @@ public void testCancelBlockedCursoredResponse() throws Exception { while (rs.next()) ++numSelectedRows; - assertEquals(false, true, "Expected exception not thrown from ResultSet.next()"); + assertEquals(false, true, TestResource.getResource("R_expectedExceptionNotThrown")); } catch (SQLException e) { - assertTrue("The query was canceled.".contains(e.getMessage()), "Unexpected exception from ResultSet.next()"); + assertTrue(TestResource.getResource("R_queryCancelled").contains(e.getMessage()), TestResource.getResource("R_unexpectedException")); } elapsedMillis += System.currentTimeMillis(); // If we get here to early, then we were able to scan through the rows too fast. // There's some slop in the elapsed time due to imprecise timer resolution. if (elapsedMillis < 2500) { - assertEquals(2500, (int) elapsedMillis, "Statement executed too quickly."); + assertEquals(2500, (int) elapsedMillis, TestResource.getResource("R_executionNotLong")); } // Looks like we were canceled. Exception message matched. Time took as long @@ -614,7 +618,7 @@ public void testCancelBlockedCursoredResponse() throws Exception { // we initially asked for. If any rows beyond the locked row were returned // then something went wrong. assertEquals(true, (numSelectedRows <= NUM_TABLE_ROWS - MIN_TABLE_ROWS), - "Too many rows returned. " + "Expected: " + (NUM_TABLE_ROWS - MIN_TABLE_ROWS) + " " + "Actual: " + numSelectedRows); + TestResource.getResource("R_valueNotMatch")); } finally { if (null != con) @@ -649,7 +653,7 @@ public void testCancelAfterResponse() throws Exception { while (rs.next()) ++numSelectedRows; rs.close(); - assertEquals(NUM_TABLE_ROWS, numSelectedRows, "Wrong number of rows returned in 1st select"); + assertEquals(NUM_TABLE_ROWS, numSelectedRows, TestResource.getResource("R_valueNotMatch")); // "Cancel" the executed query stmt.cancel(); @@ -660,7 +664,7 @@ public void testCancelAfterResponse() throws Exception { while (rs.next()) ++numSelectedRows; rs.close(); - assertEquals(NUM_TABLE_ROWS, numSelectedRows, "Wrong number of rows returned in 2nd select"); + assertEquals(NUM_TABLE_ROWS, numSelectedRows, TestResource.getResource("R_valueNotMatch")); stmt.close(); con.close(); @@ -920,11 +924,11 @@ public void testIsCloseOnCompletion() throws Exception { } catch (Exception e) { - throw new SQLException("testIsCloseOnCompletion threw exception: ", e); + throw new SQLException(TestResource.getResource("R_unexpectedException") + ": ", e); } - assertEquals(false, result, "isCloseOnCompletion default should be false."); + assertEquals(false, result, "isCloseOnCompletion: " + TestResource.getResource("R_incorrectDefault")); ps.close(); con.close(); @@ -941,8 +945,7 @@ public void testCloseOnCompletion() throws Exception { ps.closeOnCompletion(); } catch (Exception e) { - - throw new SQLException("testCloseOnCompletion threw exception: ", e); + throw new SQLException(TestResource.getResource("R_unexpectedException") + ": ", e); } @@ -955,7 +958,7 @@ public void testCloseOnCompletion() throws Exception { log.fine("testIsCloseOnCompletion threw: " + e.getMessage()); } - assertEquals(ps.isClosed(), true, "testCloseOnCompletion: statement should be closed since resultset is closed."); + assertEquals(ps.isClosed(), true, TestResource.getResource("R_statementShouldBeClosed")); con.close(); } @@ -986,11 +989,11 @@ public void testIsCloseOnCompletion() throws Exception { } catch (Exception e) { - throw new SQLException("testIsCloseOnCompletion threw exception: ", e); + throw new SQLException(TestResource.getResource("R_unexpectedException") + ": ", e); } - assertEquals(true, stmt.isCloseOnCompletion(), "isCloseOnCompletion should have been enabled."); + assertEquals(true, stmt.isCloseOnCompletion(), "isCloseOnCompletion " + TestResource.getResource("R_shouldBeEnabled")); stmt.close(); con.close(); @@ -1010,18 +1013,18 @@ public void testCloseOnCompletion() throws Exception { } catch (Exception e) { - throw new SQLException("testCloseOnCompletion threw exception: ", e); + throw new SQLException(TestResource.getResource("R_unexpectedException") + ": ", e); } ResultSet rs; rs = stmt.executeQuery("SELECT 1"); - assertEquals(stmt.isClosed(), false, "testCloseOnCompletion: statement should be open since resultset is open."); + assertEquals(stmt.isClosed(), false, TestResource.getResource("R_statementShouldBeOpened")); // now statement should be closed rs.close(); - assertEquals(stmt.isClosed(), true, "testCloseOnCompletion: statement should be closed since resultset is closed."); + assertEquals(stmt.isClosed(), true, TestResource.getResource("R_statementShouldBeClosed")); con.close(); } @@ -1043,9 +1046,7 @@ public void testConsecutiveQueries() throws Exception { stmt.closeOnCompletion(); } catch (Exception e) { - - throw new SQLException("testCloseOnCompletion threw exception: ", e); - + throw new SQLException(TestResource.getResource("R_unexpectedException") + ": ", e); } try { @@ -1068,9 +1069,7 @@ public void testConsecutiveQueries() throws Exception { ResultSet rs2 = stmt.executeQuery("SELECT * FROM " + table2Name); } catch (Exception e) { - - assertEquals(stmt.isClosed(), true, "testCloseOnCompletion: statement should be closed since previous resultset was closed."); - + assertEquals(stmt.isClosed(), true, TestResource.getResource("R_statementShouldBeClosed")); } con.close(); @@ -1083,7 +1082,7 @@ public void testConsecutiveQueries() throws Exception { */ @Test public void testLargeMaxRowsJDBC41() throws Exception { - assumeTrue("JDBC41".equals(Utils.getConfiguredProperty("JDBC_Version")), "Aborting test case as JDBC version is not compatible. "); + assumeTrue("JDBC41".equals(Utils.getConfiguredProperty("JDBC_Version")), TestResource.getResource("R_incompatJDBC")); Connection con = DriverManager.getConnection(connectionString); SQLServerStatement stmt = (SQLServerStatement) con.createStatement(); @@ -1092,7 +1091,7 @@ public void testLargeMaxRowsJDBC41() throws Exception { try { stmt.getLargeMaxRows(); - throw new SQLException("ERROR: We should not be here."); + throw new SQLException(TestResource.getResource("R_unexpectedException")); } catch (Exception e) { fail(e.getMessage()); @@ -1101,7 +1100,7 @@ public void testLargeMaxRowsJDBC41() throws Exception { // testing exception for setLargeMaxRows method try { stmt.setLargeMaxRows(2015); - throw new SQLException("ERROR: We should not be here."); + throw new SQLException(TestResource.getResource("R_unexpectedException")); } catch (Exception e) { fail(e.getMessage()); @@ -1122,14 +1121,13 @@ public void testLargeMaxRowsJDBC41() throws Exception { */ @Test public void testLargeMaxRowsJDBC42() throws Exception { - assumeTrue("JDBC42".equals(Utils.getConfiguredProperty("JDBC_Version")), "Aborting test case as JDBC version is not compatible. "); - + assumeTrue("JDBC42".equals(Utils.getConfiguredProperty("JDBC_Version")), TestResource.getResource("R_incompatJDBC")); Connection dbcon = DriverManager.getConnection(connectionString); Statement dbstmt = dbcon.createStatement(); // Default value should return zero long actual = dbstmt.getLargeMaxRows(); - assertEquals(actual, (long) 0, "getLargeMaxRows() : default value is not zero"); + assertEquals(actual, (long) 0, "getLargeMaxRows():" + TestResource.getResource("R_incorrectDefault")); // Set a new value less than MAX_VALUE, and then get the modified value long newValue = 2012L; @@ -1149,7 +1147,7 @@ public void testLargeMaxRowsJDBC42() throws Exception { assertEquals( ("calling setLargeMaxRows failed : java.lang.UnsupportedOperationException: " + "The supported maximum row count for a result set is Integer.MAX_VALUE or less."), - (e.getMessage()), "Wring setLargeMaxRows() Exception"); + (e.getMessage()), TestResource.getResource("R_unexpectedException")); } // Set a negative value. If negative is accepted, throw exception @@ -1161,7 +1159,7 @@ public void testLargeMaxRowsJDBC42() throws Exception { assertEquals( "calling setLargeMaxRows failed : com.microsoft.sqlserver.jdbc.SQLServerException: " + "The maximum row count -2,012 for a result set must be non-negative.", - e.getMessage(), "Wring setLargeMaxRows() Exception"); + e.getMessage(), TestResource.getResource("R_unexpectedException")); } if (null != dbstmt) { @@ -1362,31 +1360,30 @@ public void testStatementOutParamGetsTwice() throws Exception { ResultSet rs = stmt.getResultSet(); if (rs != null) { rs.close(); - assertEquals(stmt.isClosed(), true, "testStatementOutParamGetsTwice: statement should be closed since resultset is closed."); + assertEquals(stmt.isClosed(), true, TestResource.getResource("R_statementShouldBeClosed")); } else { - assertEquals(stmt.isClosed(), false, "testStatementOutParamGetsTwice: statement should be open since no resultset."); + assertEquals(stmt.isClosed(), false, TestResource.getResource("R_statementShouldBeOpened")); } CallableStatement cstmt = con.prepareCall("{ ? = CALL " + procNameTemp + " (?,?)}"); cstmt.registerOutParameter(1, Types.INTEGER); cstmt.setObject(2, Short.valueOf("32"), Types.SMALLINT); cstmt.registerOutParameter(3, Types.SMALLINT); cstmt.execute(); - assertEquals(cstmt.getInt(1), 33, "Wrong value"); - assertEquals(cstmt.getInt(3), 32, "Wrong value"); + assertEquals(cstmt.getInt(1), 33, TestResource.getResource("R_valueNotMatch")); + assertEquals(cstmt.getInt(3), 32, TestResource.getResource("R_valueNotMatch")); cstmt.setObject(2, Short.valueOf("34"), Types.SMALLINT); cstmt.execute(); - assertEquals(cstmt.getInt(1), 35, "Wrong value"); - assertEquals(cstmt.getInt(3), 34, "Wrong value"); + assertEquals(cstmt.getInt(1), 35, TestResource.getResource("R_valueNotMatch")); + assertEquals(cstmt.getInt(3), 34, TestResource.getResource("R_valueNotMatch")); rs = cstmt.getResultSet(); if (rs != null) { rs.close(); - assertEquals(stmt.isClosed(), true, "testStatementOutParamGetsTwice: statement should be closed since resultset is closed."); - + assertEquals(stmt.isClosed(), true, TestResource.getResource("R_statementShouldBeClosed")); } else { - assertEquals((stmt).isClosed(), false, "testStatementOutParamGetsTwice: statement should be open since no resultset."); + assertEquals(stmt.isClosed(), false, TestResource.getResource("R_statementShouldBeOpened")); } } @@ -1405,16 +1402,16 @@ public void testStatementOutManyParamGetsTwiceRandomOrder() throws Exception { cstmt.setObject(4, Short.valueOf("23"), Types.SMALLINT); cstmt.registerOutParameter(5, Types.INTEGER); cstmt.execute(); - assertEquals(cstmt.getInt(1), 33, "Wrong value"); - assertEquals(cstmt.getInt(5), 23, "Wrong value"); - assertEquals(cstmt.getInt(3), 32, "Wrong value"); + assertEquals(cstmt.getInt(1), 33, TestResource.getResource("R_valueNotMatch")); + assertEquals(cstmt.getInt(5), 23, TestResource.getResource("R_valueNotMatch")); + assertEquals(cstmt.getInt(3), 32, TestResource.getResource("R_valueNotMatch")); cstmt.setObject(2, Short.valueOf("34"), Types.SMALLINT); cstmt.setObject(4, Short.valueOf("24"), Types.SMALLINT); cstmt.execute(); - assertEquals(cstmt.getInt(3), 34, "Wrong value"); - assertEquals(cstmt.getInt(5), 24, "Wrong value"); - assertEquals(cstmt.getInt(1), 35, "Wrong value"); + assertEquals(cstmt.getInt(3), 34, TestResource.getResource("R_valueNotMatch")); + assertEquals(cstmt.getInt(5), 24, TestResource.getResource("R_valueNotMatch")); + assertEquals(cstmt.getInt(1), 35, TestResource.getResource("R_valueNotMatch")); } /** @@ -1436,13 +1433,13 @@ public void testStatementOutParamGetsTwiceInOut() throws Exception { cstmt.setObject(3, Short.valueOf("100"), Types.SMALLINT); cstmt.registerOutParameter(3, Types.SMALLINT); cstmt.execute(); - assertEquals(cstmt.getInt(1), 2, "Wrong value"); - assertEquals(cstmt.getInt(3), 101, "Wrong value"); + assertEquals(cstmt.getInt(1), 2, TestResource.getResource("R_valueNotMatch")); + assertEquals(cstmt.getInt(1), 2, TestResource.getResource("R_valueNotMatch")); cstmt.setObject(2, Short.valueOf("10"), Types.SMALLINT); cstmt.execute(); - assertEquals(cstmt.getInt(1), 11, "Wrong value"); - assertEquals(cstmt.getInt(3), 101, "Wrong value"); + assertEquals(cstmt.getInt(1), 11, TestResource.getResource("R_valueNotMatch")); + assertEquals(cstmt.getInt(3), 101, TestResource.getResource("R_valueNotMatch")); } /** @@ -1468,8 +1465,8 @@ public void testResultSetParams() throws Exception { cstmt.registerOutParameter(2, java.sql.Types.VARCHAR); ResultSet rs = cstmt.executeQuery(); rs.next(); - assertEquals(rs.getString(2), "hello", "Wrong value"); - assertEquals(cstmt.getString(2), "hi", "Wrong value"); + assertEquals(rs.getString(2), "hello", TestResource.getResource("R_valueNotMatch")); + assertEquals(cstmt.getString(2), "hi", TestResource.getResource("R_valueNotMatch")); } /** @@ -1563,7 +1560,7 @@ public void testResultSetErrors() throws Exception { } ; - assertEquals(null, cstmt.getString(2), "Wrong value"); + assertEquals(null, cstmt.getString(2), TestResource.getResource("R_valueNotMatch")); } /** @@ -1608,15 +1605,13 @@ public void testRowError() throws Exception { cstmt.closeOnCompletion(); } catch (Exception e) { - - throw new SQLException("testRowError threw exception: ", e); - + throw new SQLException(TestResource.getResource("R_unexpectedException")); } ResultSet rs = cstmt.executeQuery(); assertEquals(true, rs.next(), "Query returned no rows"); rs.close(); - assertEquals(cstmt.isClosed(), true, "testRowError: statement should be closed since resultset is closed."); + assertEquals(cstmt.isClosed(), true, TestResource.getResource("R_statementShouldBeClosed")); // On a second connection, repeat the query, with an immediate // lock timeout to induce an error. @@ -1643,11 +1638,11 @@ public void testRowError() throws Exception { for (int i = 0; i < 2; i++) { try { rs.next(); - assertEquals(false, true, "Expected row lock timeout exception not thrown"); + assertEquals(false, true, "lock timeout" + TestResource.getResource("R_expectedExceptionNotThrown")); } catch (SQLException e) { assertEquals(1222, // lock timeout - e.getErrorCode(), "Wrong exception from ResultSet.next: " + e.getMessage()); + e.getErrorCode(), TestResource.getResource("R_unexpectedException") + e.getMessage()); } } @@ -2052,10 +2047,10 @@ public void testActiveStatement() throws Exception { assertEquals(stmt.isClosed(), false, "Wrong return value from Statement.isClosed"); } catch (UnsupportedOperationException e) { - assertEquals(e.getMessage(), "This operation is not supported.", "Wrong exception message"); + assertEquals(e.getMessage(), TestResource.getResource("R_unexpectedException"), e.getMessage()); } - assertEquals(stmt.isClosed(), false, "testActiveStatement: statement should be open since resultset is open."); + assertEquals(stmt.isClosed(), false, TestResource.getResource("R_statementShouldBeOpened")); stmt.close(); conn.close(); } @@ -2077,7 +2072,7 @@ public void testClosedStatement() throws Exception { assertEquals(stmt.isClosed(), true, "Wrong return value from Statement.isClosed"); } catch (UnsupportedOperationException e) { - assertEquals(e.getMessage(), "This operation is not supported.", "Wrong exception message"); + assertEquals(e.getMessage(), TestResource.getResource("R_unexpectedException"), e.getMessage()); } conn.close(); @@ -2100,7 +2095,7 @@ public void testClosedConnection() throws Exception { assertEquals(stmt.isClosed(), true, "Wrong return value from Statement.isClosed"); } catch (UnsupportedOperationException e) { - assertEquals(e.getMessage(), "This operation is not supported.", "Wrong exception message"); + assertEquals(e.getMessage(), TestResource.getResource("R_unexpectedException"), e.getMessage()); } } } @@ -2124,9 +2119,7 @@ public void testActiveResultSet() throws Exception { stmt.closeOnCompletion(); } catch (Exception e) { - - throw new SQLException("testActiveResultSet threw exception: ", e); - + throw new SQLException(TestResource.getResource("R_unexpectedException")); } SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT 1"); @@ -2135,11 +2128,11 @@ public void testActiveResultSet() throws Exception { assertEquals(rs.isClosed(), false, "Wrong return value from ResultSet.isClosed"); } catch (UnsupportedOperationException e) { - assertEquals(e.getMessage(), "This operation is not supported.", "Wrong exception message"); + assertEquals(e.getMessage(), TestResource.getResource("R_unexpectedException"), e.getMessage()); } rs.close(); - assertEquals(stmt.isClosed(), true, "testActiveResultSet: statement should be closed since resultset is closed."); + assertEquals(stmt.isClosed(), true, TestResource.getResource("R_statementShouldBeClosed")); conn.close(); } @@ -2161,9 +2154,7 @@ public void testClosedResultSet() throws Exception { stmt.closeOnCompletion(); } catch (Exception e) { - - throw new SQLException("testClosedResultSet threw exception: ", e); - + throw new SQLException(TestResource.getResource("R_unexpectedException")); } SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT 1"); @@ -2173,9 +2164,9 @@ public void testClosedResultSet() throws Exception { assertEquals(rs.isClosed(), true, "Wrong return value from ResultSet.isClosed"); } catch (UnsupportedOperationException e) { - assertEquals(e.getMessage(), "This operation is not supported.", "Wrong exception message"); + assertEquals(e.getMessage(), TestResource.getResource("R_unexpectedException"), e.getMessage()); } - assertEquals(stmt.isClosed(), true, "testClosedResultSet: statement should be closed since resultset is closed."); + assertEquals(stmt.isClosed(), true, TestResource.getResource("R_statementShouldBeClosed")); conn.close(); } @@ -2198,7 +2189,7 @@ public void testClosedStatement() throws Exception { assertEquals(rs.isClosed(), true, "Wrong return value from ResultSet.isClosed"); } catch (UnsupportedOperationException e) { - assertEquals(e.getMessage(), "This operation is not supported.", "Wrong exception message"); + assertEquals(e.getMessage(), TestResource.getResource("R_unexpectedException"), e.getMessage()); } conn.close(); @@ -2223,7 +2214,7 @@ public void testClosedConnection() throws Exception { assertEquals(rs.isClosed(), true, "Wrong return value from ResultSet.isClosed"); } catch (UnsupportedOperationException e) { - assertEquals(e.getMessage(), "This operation is not supported.", "Wrong exception message"); + assertEquals(e.getMessage(), TestResource.getResource("R_unexpectedException"), e.getMessage()); } } } @@ -2426,9 +2417,7 @@ public void testUpdateCountAfterRaiseError() throws Exception { pstmt.closeOnCompletion(); } catch (Exception e) { - - throw new SQLException("testUpdateCountAfterRaiseError threw exception: ", e); - + throw new SQLException(TestResource.getResource("R_unexpectedException")); } boolean result = pstmt.execute(); @@ -2438,7 +2427,7 @@ public void testUpdateCountAfterRaiseError() throws Exception { try { result = pstmt.getMoreResults(); - assertEquals(true, false, "Second result: Expected SQLException not thrown"); + assertEquals(true, false, TestResource.getResource("R_expectedExceptionNotThrown")); } catch (SQLException e) { String expectedMessage; @@ -2464,7 +2453,7 @@ public void testUpdateCountAfterRaiseError() throws Exception { assertEquals(rowCount, NUM_ROWS, "Third result: wrong number of rows returned"); rs.close(); - assertEquals(pstmt.isClosed(), true, "testUpdateCountAfterRaiseError: statement should be closed since resultset is closed."); + assertEquals(pstmt.isClosed(), true, TestResource.getResource("R_statementShouldBeClosed")); con.close(); } @@ -2487,7 +2476,7 @@ public void testUpdateCountAfterErrorInTriggerLastUpdateCountFalse() throws Exce try { result = pstmt.getMoreResults(); - assertEquals(true, false, "Second result: Expected SQLException not thrown"); + assertEquals(true, false, TestResource.getResource("R_expectedExceptionNotThrown")); } catch (SQLException e) { String expectedMessage; @@ -2512,7 +2501,7 @@ public void testUpdateCountAfterErrorInTriggerLastUpdateCountFalse() throws Exce ++rowCount; assertEquals(rowCount, NUM_ROWS, "Wrong number of rows in table"); assertEquals(pstmt.isClosed(), false, - "testUpdateCountAfterErrorInTrigger_LastUpdateCountFalse: statement should be open since resultset is not closed."); + TestResource.getResource("R_statementShouldBeOpened")); rs.close(); pstmt.close(); @@ -2532,7 +2521,7 @@ public void testUpdateCountAfterErrorInTriggerLastUpdateCountTrue() throws Excep try { pstmt.executeUpdate(); - assertEquals(true, false, "First result: Expected SQLException not thrown"); + assertEquals(true, false, TestResource.getResource("R_expectedExceptionNotThrown")); } catch (SQLException e) { String expectedMessage; @@ -2595,15 +2584,13 @@ private void setup() throws Exception { stmt.closeOnCompletion(); } catch (Exception e) { - - throw new SQLException("setup threw exception: ", e); - + throw new SQLException(TestResource.getResource("R_unexpectedException"), e); } stmt.executeUpdate("CREATE TABLE " + tableName + " (col1 INT primary key)"); for (int i = 0; i < NUM_ROWS; i++) stmt.executeUpdate("INSERT INTO " + tableName + " (col1) VALUES (" + i + ")"); - assertEquals(stmt.isClosed(), false, "setup: statement should be open since resultset not closed."); + assertEquals(stmt.isClosed(), false, TestResource.getResource("R_statementShouldBeOpened")); stmt.close(); con.commit(); con.close(); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/WrapperTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/WrapperTest.java index 754ceca1d..8109eb551 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/WrapperTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/WrapperTest.java @@ -15,6 +15,7 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; +import java.text.MessageFormat; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; @@ -23,6 +24,7 @@ import com.microsoft.sqlserver.jdbc.ISQLServerPreparedStatement; import com.microsoft.sqlserver.jdbc.SQLServerCallableStatement; import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBConnection; @@ -46,25 +48,32 @@ public void wrapTest() throws Exception { try { // First make sure that a statement can be unwrapped boolean isWrapper = ((SQLServerStatement) stmt).isWrapperFor(Class.forName("com.microsoft.sqlserver.jdbc.SQLServerStatement")); - assertEquals(isWrapper, true, "SQLServerStatement should be a wrapper for self"); + + MessageFormat form = new MessageFormat(TestResource.getResource("R_shouldBeWrapper")); + MessageFormat form2 = new MessageFormat(TestResource.getResource("R_shouldNotBeWrapper")); + Object[][] msgArgs = {{"SQLStatement", "self"}, {"SQLServerStatement", "ISQLServerStatement"}, {"SQLServerCallableStatement", "SQLServerStatement"}, {"SQLServerCallableStatement", "SQLServerStatement"}}; + + assertEquals(isWrapper, true, form.format(msgArgs[0])); + isWrapper = ((SQLServerStatement) stmt).isWrapperFor(Class.forName("com.microsoft.sqlserver.jdbc.ISQLServerStatement")); - assertEquals(isWrapper, true, "SQLServerStatement should be a wrapper for ISQLServerStatement"); + assertEquals(isWrapper, true, form.format(msgArgs[1])); isWrapper = ((SQLServerStatement) stmt).isWrapperFor(Class.forName("com.microsoft.sqlserver.jdbc.SQLServerConnection")); - assertEquals(isWrapper, false, "SQLServerStatement should not be a wrapper for SQLServerConnection"); + assertEquals(isWrapper, false, form2.format(msgArgs[1])); // Now make sure that we can unwrap a SQLServerCallableStatement to a SQLServerStatement CallableStatement cs = con.prepareCall("{ ? = CALL " + "ProcName" + " (?, ?, ?, ?) }"); // Test the class first isWrapper = ((SQLServerCallableStatement) cs).isWrapperFor(Class.forName("com.microsoft.sqlserver.jdbc.SQLServerStatement")); - assertEquals(isWrapper, true, "SQLServerCallableStatement should be a wrapper for SQLServerStatement"); + assertEquals(isWrapper, true, form.format(msgArgs[2])); // Now unwrap the Callable to a statement and call a SQLServerStatement specific function and make sure it succeeds. SQLServerStatement stmt2 = (SQLServerStatement) ((SQLServerCallableStatement) cs) .unwrap(Class.forName("com.microsoft.sqlserver.jdbc.SQLServerStatement")); stmt2.setResponseBuffering("adaptive"); // now test the interface isWrapper = ((SQLServerCallableStatement) cs).isWrapperFor(Class.forName("com.microsoft.sqlserver.jdbc.ISQLServerCallableStatement")); - assertEquals(isWrapper, true, "SQLServerCallableStatement should be a wrapper for ISQLServerCallableStatement"); + assertEquals(isWrapper, true, form.format(msgArgs[1])); + // Now unwrap the Callable to a statement and call a SQLServerStatement specific function and make sure it succeeds. ISQLServerPreparedStatement stmt4 = (ISQLServerPreparedStatement) ((SQLServerCallableStatement) cs) .unwrap(Class.forName("com.microsoft.sqlserver.jdbc.ISQLServerPreparedStatement")); @@ -75,7 +84,7 @@ public void wrapTest() throws Exception { // Try Unwrapping CallableStatement to a callableStatement isWrapper = ((SQLServerCallableStatement) cs).isWrapperFor(Class.forName("com.microsoft.sqlserver.jdbc.SQLServerCallableStatement")); - assertEquals(isWrapper, true, "SQLServerCallableStatement should be a wrapper for SQLServerCallableStatement"); + assertEquals(isWrapper, true, form.format(msgArgs[3])); // Now unwrap the Callable to a SQLServerCallableStatement and call a SQLServerStatement specific function and make sure it succeeds. SQLServerCallableStatement stmt3 = (SQLServerCallableStatement) ((SQLServerCallableStatement) cs) .unwrap(Class.forName("com.microsoft.sqlserver.jdbc.SQLServerCallableStatement")); @@ -92,8 +101,8 @@ public void wrapTest() throws Exception { } catch (UnsupportedOperationException e) { - assertEquals(System.getProperty("java.specification.version"), "1.5", "isWrapperFor should be supported in anything other than 1.5"); - assertTrue(e.getMessage().equalsIgnoreCase("This operation is not supported."), "Wrong exception message"); + assertEquals(System.getProperty("java.specification.version"), "1.5", "isWrapperFor " + TestResource.getResource("R_shouldBeSupported")); + assertTrue(e.getMessage().equalsIgnoreCase("This operation is not supported."), TestResource.getResource("R_unexpectedExceptionContent")); } finally { if (null != stmt) { @@ -122,7 +131,7 @@ public void unWrapFailureTest() throws Exception { stmt.unwrap(Class.forName(str)); assertEquals(isWrapper, false, "SQLServerStatement should not be a wrapper for string"); stmt.unwrap(Class.forName(str)); - assertTrue(false, "An exception should have been thrown. This code should not be reached"); + assertTrue(false, TestResource.getResource("R_exceptionNotThrown")); } catch (SQLException ex) { Throwable t = ex.getCause(); @@ -130,8 +139,8 @@ public void unWrapFailureTest() throws Exception { assertEquals(t.getClass(), exceptionClass, "The cause in the exception class does not match"); } catch (UnsupportedOperationException e) { - assertEquals(System.getProperty("java.specification.version"), "1.5", "isWrapperFor should be supported in anything other than 1.5"); - assertEquals(e.getMessage(), "This operation is not supported.", "Wrong exception message"); + assertEquals(System.getProperty("java.specification.version"), "1.5", "isWrapperFor " + TestResource.getResource("R_shouldBeSupported")); + assertEquals(e.getMessage(), "This operation is not supported.", TestResource.getResource("R_unexpectedExceptionContent")); } finally { if (null != stmt) { From 8d044b1d111d81e8f51158e18069ed5a7ebc417c Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 29 May 2018 17:30:43 -0700 Subject: [PATCH 40/84] undo some changes made to SQLServerConnection --- .../sqlserver/jdbc/SQLServerConnection.java | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 01d4a9b70..a4a228fa0 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -117,19 +117,20 @@ public class SQLServerConnection implements ISQLServerConnection { private SqlFedAuthToken fedAuthToken = null; + private String originalHostNameInCertificate = null; + private Boolean isAzureDW = null; static class Sha1HashKey { private byte[] bytes; Sha1HashKey(String sql, - String parametersDefinition, - String dbName) { - this(String.format("%s%s%s", sql, parametersDefinition, dbName)); + String parametersDefinition) { + this(String.format("%s%s", sql, parametersDefinition)); } Sha1HashKey(String s) { - bytes = getSha1Digest().digest(s.getBytes()); + bytes = getSha1Digest().digest(s.getBytes()); } public boolean equals(Object obj) { @@ -1210,6 +1211,23 @@ Connection connectInternal(Properties propsIn, pooledConnectionParent = pooledConnection; + String hostNameInCertificate = activeConnectionProperties. + getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString()); + + // hostNameInCertificate property can change when redirection is involved, so maintain this value + // for every instance of SQLServerConnection. + if (null == originalHostNameInCertificate && null != hostNameInCertificate && !hostNameInCertificate.isEmpty()) { + originalHostNameInCertificate = activeConnectionProperties. + getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString()); + } + + if (null != originalHostNameInCertificate && !originalHostNameInCertificate.isEmpty()) { + // if hostNameInCertificate has a legitimate value (and not empty or null), + // reset hostNameInCertificate to the original value every time we connect (or re-connect). + activeConnectionProperties.setProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString(), + originalHostNameInCertificate); + } + String sPropKey; String sPropValue; @@ -5739,18 +5757,18 @@ final void unprepareUnreferencedPreparedStatementHandles(boolean force) { } /** - * Returns true if statement pooling is disabled. + * Determine whether statement pooling is disabled. * - * @return + * @return true if statement pooling is disabled, false if it is enabled. */ public boolean getDisableStatementPooling() { return this.disableStatementPooling; } /** - * Sets statement pooling to true or false; + * Disable/enable statement pooling. * - * @param value + * @param value true to disable statement pooling, false to enable it. */ public void setDisableStatementPooling(boolean value) { this.disableStatementPooling = value; From 70bea50cc2349035b4efb3f524db707c5e6f021a Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 29 May 2018 17:50:18 -0700 Subject: [PATCH 41/84] apply resource bundling changes --- .../statement/BatchExecuteWithErrorsTest.java | 112 ++++++++++-------- 1 file changed, 61 insertions(+), 51 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java index d86e08c39..749b41b91 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java @@ -30,6 +30,7 @@ import org.junit.runner.RunWith; import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBConnection; @@ -142,7 +143,7 @@ public void Repro47239() throws SQLException { log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test interleaved inserts and warnings"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_testInterleaved")); expectedUpdateCounts = new int[] {-3, 1, 1, 1}; stmt.addBatch(error); @@ -162,7 +163,7 @@ public void Repro47239() throws SQLException { log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test error followed by inserts"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_errorFollowInserts")); // 50280 expectedUpdateCounts = new int[] {1, -3}; stmt.addBatch(insertStmt); @@ -179,7 +180,7 @@ public void Repro47239() throws SQLException { log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test insert followed by non-fatal error (50280)"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_errorFollow50280")); // Test "soft" errors conn.setAutoCommit(false); @@ -189,12 +190,13 @@ public void Repro47239() throws SQLException { stmt.addBatch(insertStmt); try { stmt.executeBatch(); - assertEquals(true, false, "Soft error test: executeBatch unexpectedly succeeded"); + // Soft error test: executeBatch unexpectedly succeeded + assertEquals(true, false, TestResource.getResource("R_shouldThrowException")); } catch (BatchUpdateException bue) { - assertEquals("A result set was generated for update.", bue.getMessage(), "Soft error test: wrong error message in BatchUpdateException"); + assertEquals("A result set was generated for update.", bue.getMessage(), TestResource.getResource("R_unexpectedExceptionContent")); assertEquals(Arrays.equals(bue.getUpdateCounts(), new int[] {-3, 1, -3, 1}), true, - "Soft error test: wrong update counts in BatchUpdateException"); + TestResource.getResource("R_incorrectUpdateCount")); } conn.rollback(); @@ -207,12 +209,12 @@ public void Repro47239() throws SQLException { stmt.executeBatch(); } catch (BatchUpdateException bue) { - assertThat(bue.getMessage(), containsString("Syntax error converting date")); + assertThat(bue.getMessage(), containsString(TestResource.getResource("R_syntaxErrorDateConvert"))); // CTestLog.CompareStartsWith(bue.getMessage(), "Syntax error converting date", "Transaction rollback with conversion error threw wrong // BatchUpdateException"); } catch (SQLException e) { - assertThat(e.getMessage(), containsString("Conversion failed when converting date")); + assertThat(e.getMessage(), containsString(TestResource.getResource("R_dateConvertError"))); // CTestLog.CompareStartsWith(e.getMessage(), "Conversion failed when converting date", "Transaction rollback with conversion error threw // wrong SQLException"); } @@ -239,19 +241,22 @@ public void Repro47239() throws SQLException { stmt.addBatch(insertStmt); try { stmt.executeBatch(); - assertEquals(false, true, "Test fatal errors batch execution succeeded (should have failed)"); + // Test fatal errors batch execution succeeded (should have failed) + assertEquals(false, true, TestResource.getResource("R_shouldThrowException")); } catch (BatchUpdateException bue) { - assertEquals(false, true, "Test fatal errors returned BatchUpdateException rather than SQLException"); + // Test fatal errors returned BatchUpdateException rather than SQLException + assertEquals(false, true, TestResource.getResource("R_unexpectedException") + bue.getMessage()); + } catch (SQLException e) { actualExceptionText = e.getMessage(); if (actualExceptionText.endsWith("reset")) { - assertTrue(actualExceptionText.equalsIgnoreCase("Connection reset"), "Test fatal errors"); + assertTrue(actualExceptionText.equalsIgnoreCase("Connection reset"), TestResource.getResource("R_unexpectedExceptionContent") + ": " + actualExceptionText); } else { - assertTrue(actualExceptionText.equalsIgnoreCase("raiserror level 20"), "Test fatal errors"); + assertTrue(actualExceptionText.equalsIgnoreCase("raiserror level 20"), TestResource.getResource("R_unexpectedExceptionContent") + ": " + actualExceptionText); } } } @@ -264,7 +269,7 @@ public void Repro47239() throws SQLException { stmt.close(); conn.close(); } - + /** * Batch test * @@ -362,7 +367,7 @@ public void Repro47239UseBulkCopyAPI() throws SQLException, NoSuchFieldException log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test interleaved inserts and warnings"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_testInterleaved")); expectedUpdateCounts = new int[] {-3, 1, 1, 1}; stmt.addBatch(error); @@ -382,7 +387,7 @@ public void Repro47239UseBulkCopyAPI() throws SQLException, NoSuchFieldException log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test error followed by inserts"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_errorFollowInserts")); // 50280 expectedUpdateCounts = new int[] {1, -3}; stmt.addBatch(insertStmt); @@ -399,7 +404,7 @@ public void Repro47239UseBulkCopyAPI() throws SQLException, NoSuchFieldException log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test insert followed by non-fatal error (50280)"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_errorFollow50280")); // Test "soft" errors conn.setAutoCommit(false); @@ -409,12 +414,12 @@ public void Repro47239UseBulkCopyAPI() throws SQLException, NoSuchFieldException stmt.addBatch(insertStmt); try { stmt.executeBatch(); - assertEquals(true, false, "Soft error test: executeBatch unexpectedly succeeded"); + assertEquals(true, false, TestResource.getResource("R_shouldThrowException")); } catch (BatchUpdateException bue) { - assertEquals("A result set was generated for update.", bue.getMessage(), "Soft error test: wrong error message in BatchUpdateException"); + assertEquals("A result set was generated for update.", bue.getMessage(), TestResource.getResource("R_unexpectedExceptionContent")); assertEquals(Arrays.equals(bue.getUpdateCounts(), new int[] {-3, 1, -3, 1}), true, - "Soft error test: wrong update counts in BatchUpdateException"); + TestResource.getResource("R_incorrectUpdateCount")); } conn.rollback(); @@ -427,12 +432,12 @@ public void Repro47239UseBulkCopyAPI() throws SQLException, NoSuchFieldException stmt.executeBatch(); } catch (BatchUpdateException bue) { - assertThat(bue.getMessage(), containsString("Syntax error converting date")); + assertThat(bue.getMessage(), containsString(TestResource.getResource("R_syntaxErrorDateConvert"))); // CTestLog.CompareStartsWith(bue.getMessage(), "Syntax error converting date", "Transaction rollback with conversion error threw wrong // BatchUpdateException"); } catch (SQLException e) { - assertThat(e.getMessage(), containsString("Conversion failed when converting date")); + assertThat(e.getMessage(), containsString(TestResource.getResource("R_dateConvertError"))); // CTestLog.CompareStartsWith(e.getMessage(), "Conversion failed when converting date", "Transaction rollback with conversion error threw // wrong SQLException"); } @@ -459,19 +464,19 @@ public void Repro47239UseBulkCopyAPI() throws SQLException, NoSuchFieldException stmt.addBatch(insertStmt); try { stmt.executeBatch(); - assertEquals(false, true, "Test fatal errors batch execution succeeded (should have failed)"); + assertEquals(false, true, TestResource.getResource("R_shouldThrowException")); } catch (BatchUpdateException bue) { - assertEquals(false, true, "Test fatal errors returned BatchUpdateException rather than SQLException"); + assertEquals(false, true, TestResource.getResource("R_unexpectedException") + bue.getMessage()); } catch (SQLException e) { actualExceptionText = e.getMessage(); if (actualExceptionText.endsWith("reset")) { - assertTrue(actualExceptionText.equalsIgnoreCase("Connection reset"), "Test fatal errors"); + assertTrue(actualExceptionText.equalsIgnoreCase("Connection reset"), TestResource.getResource("R_unexpectedExceptionContent") + ": " + actualExceptionText); } else { - assertTrue(actualExceptionText.equalsIgnoreCase("raiserror level 20"), "Test fatal errors"); + assertTrue(actualExceptionText.equalsIgnoreCase("raiserror level 20"), TestResource.getResource("R_unexpectedExceptionContent") + ": " + actualExceptionText); } } } @@ -494,7 +499,7 @@ public void Repro47239UseBulkCopyAPI() throws SQLException, NoSuchFieldException @DisplayName("Regression test for using 'large' methods") public void Repro47239large() throws Exception { - assumeTrue("JDBC42".equals(Utils.getConfiguredProperty("JDBC_Version")), "Aborting test case as JDBC version is not compatible. "); + assumeTrue("JDBC42".equals(Utils.getConfiguredProperty("JDBC_Version")), TestResource.getResource("R_incompatJDBC")); // the DBConnection for detecting whether the server is SQL Azure or SQL Server. con = DriverManager.getConnection(connectionString); final String warning; @@ -566,7 +571,7 @@ public void Repro47239large() throws Exception { log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test interleaved inserts and warnings"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_testInterleaved")); expectedUpdateCounts = new long[] {-3, 1, 1, 1}; stmt.addBatch(error); @@ -586,7 +591,7 @@ public void Repro47239large() throws Exception { log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test error followed by inserts"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_errorFollowInserts")); // 50280 expectedUpdateCounts = new long[] {1, -3}; @@ -604,7 +609,7 @@ public void Repro47239large() throws Exception { log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test insert followed by non-fatal error (50280)"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_errorFollow50280")); // Test "soft" errors conn.setAutoCommit(false); @@ -614,12 +619,15 @@ public void Repro47239large() throws Exception { stmt.addBatch(insertStmt); try { stmt.executeLargeBatch(); - assertEquals(false, true, "Soft error test: executeLargeBatch unexpectedly succeeded"); + // Soft error test: executeLargeBatch unexpectedly succeeded + assertEquals(false, true, TestResource.getResource("R_shouldThrowException")); } catch (BatchUpdateException bue) { - assertEquals("A result set was generated for update.", bue.getMessage(), "Soft error test: wrong error message in BatchUpdateException"); + // Soft error test: wrong error message in BatchUpdateException + assertEquals("A result set was generated for update.", bue.getMessage(), TestResource.getResource("R_unexpectedExceptionContent")); + // Soft error test: wrong update counts in BatchUpdateException assertEquals(Arrays.equals(bue.getLargeUpdateCounts(), new long[] {-3, 1, -3, 1}), true, - "Soft error test: wrong update counts in BatchUpdateException"); + TestResource.getResource("R_incorrectUpdateCount")); } conn.rollback(); @@ -632,10 +640,10 @@ public void Repro47239large() throws Exception { stmt.executeLargeBatch(); } catch (BatchUpdateException bue) { - assertThat(bue.getMessage(), containsString("Syntax error converting date")); + assertThat(bue.getMessage(), containsString(TestResource.getResource("R_syntaxErrorDateConvert"))); } catch (SQLException e) { - assertThat(e.getMessage(), containsString("Conversion failed when converting date")); + assertThat(e.getMessage(), containsString(TestResource.getResource("R_dateConvertError"))); } conn.setAutoCommit(true); @@ -659,19 +667,21 @@ public void Repro47239large() throws Exception { stmt.addBatch(insertStmt); try { stmt.executeLargeBatch(); - assertEquals(false, true, "Test fatal errors batch execution succeeded (should have failed)"); + // Test fatal errors batch execution succeeded (should have failed) + assertEquals(false, true, TestResource.getResource("R_shouldThrowException")); } catch (BatchUpdateException bue) { - assertEquals(false, true, "Test fatal errors returned BatchUpdateException rather than SQLException"); + // Test fatal errors returned BatchUpdateException rather than SQLException + assertEquals(false, true, TestResource.getResource("R_unexpectedException") + bue.getMessage()); } catch (SQLException e) { actualExceptionText = e.getMessage(); if (actualExceptionText.endsWith("reset")) { - assertTrue(actualExceptionText.equalsIgnoreCase("Connection reset"), "Test fatal errors"); + assertTrue(actualExceptionText.equalsIgnoreCase("Connection reset"), TestResource.getResource("R_unexpectedExceptionContent") + ": " + actualExceptionText); } else { - assertTrue(actualExceptionText.equalsIgnoreCase("raiserror level 20"), "Test fatal errors"); + assertTrue(actualExceptionText.equalsIgnoreCase("raiserror level 20"), TestResource.getResource("R_unexpectedExceptionContent") + ": " + actualExceptionText); } } @@ -695,7 +705,7 @@ public void Repro47239large() throws Exception { @DisplayName("Regression test for using 'large' methods") public void Repro47239largeUseBulkCopyAPI() throws Exception { - assumeTrue("JDBC42".equals(Utils.getConfiguredProperty("JDBC_Version")), "Aborting test case as JDBC version is not compatible. "); + assumeTrue("JDBC42".equals(Utils.getConfiguredProperty("JDBC_Version")), TestResource.getResource("R_incompatJDBC")); // the DBConnection for detecting whether the server is SQL Azure or SQL Server. con = DriverManager.getConnection(connectionString); Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); @@ -770,7 +780,7 @@ public void Repro47239largeUseBulkCopyAPI() throws Exception { log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test interleaved inserts and warnings"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_testInterleaved")); expectedUpdateCounts = new long[] {-3, 1, 1, 1}; stmt.addBatch(error); @@ -790,7 +800,7 @@ public void Repro47239largeUseBulkCopyAPI() throws Exception { log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test error followed by inserts"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_errorFollowInserts")); // 50280 expectedUpdateCounts = new long[] {1, -3}; @@ -808,7 +818,7 @@ public void Repro47239largeUseBulkCopyAPI() throws Exception { log.fine("" + updateCount + ","); } log.fine(""); - assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), "Test insert followed by non-fatal error (50280)"); + assertTrue(Arrays.equals(actualUpdateCounts, expectedUpdateCounts), TestResource.getResource("R_errorFollow50280")); // Test "soft" errors conn.setAutoCommit(false); @@ -818,12 +828,12 @@ public void Repro47239largeUseBulkCopyAPI() throws Exception { stmt.addBatch(insertStmt); try { stmt.executeLargeBatch(); - assertEquals(false, true, "Soft error test: executeLargeBatch unexpectedly succeeded"); + assertEquals(false, true, TestResource.getResource("R_shouldThrowException")); } catch (BatchUpdateException bue) { - assertEquals("A result set was generated for update.", bue.getMessage(), "Soft error test: wrong error message in BatchUpdateException"); + assertEquals("A result set was generated for update.", bue.getMessage(), TestResource.getResource("R_unexpectedExceptionContent")); assertEquals(Arrays.equals(bue.getLargeUpdateCounts(), new long[] {-3, 1, -3, 1}), true, - "Soft error test: wrong update counts in BatchUpdateException"); + TestResource.getResource("R_incorrectUpdateCount")); } conn.rollback(); @@ -836,10 +846,10 @@ public void Repro47239largeUseBulkCopyAPI() throws Exception { stmt.executeLargeBatch(); } catch (BatchUpdateException bue) { - assertThat(bue.getMessage(), containsString("Syntax error converting date")); + assertThat(bue.getMessage(), containsString(TestResource.getResource("R_syntaxErrorDateConvert"))); } catch (SQLException e) { - assertThat(e.getMessage(), containsString("Conversion failed when converting date")); + assertThat(e.getMessage(), containsString(TestResource.getResource("R_dateConvertError"))); } conn.setAutoCommit(true); @@ -863,19 +873,19 @@ public void Repro47239largeUseBulkCopyAPI() throws Exception { stmt.addBatch(insertStmt); try { stmt.executeLargeBatch(); - assertEquals(false, true, "Test fatal errors batch execution succeeded (should have failed)"); + assertEquals(false, true, TestResource.getResource("R_shouldThrowException")); } catch (BatchUpdateException bue) { - assertEquals(false, true, "Test fatal errors returned BatchUpdateException rather than SQLException"); + assertEquals(false, true, TestResource.getResource("R_unexpectedException") + bue.getMessage()); } catch (SQLException e) { actualExceptionText = e.getMessage(); if (actualExceptionText.endsWith("reset")) { - assertTrue(actualExceptionText.equalsIgnoreCase("Connection reset"), "Test fatal errors"); + assertTrue(actualExceptionText.equalsIgnoreCase("Connection reset"), TestResource.getResource("R_unexpectedExceptionContent") + ": " + actualExceptionText); } else { - assertTrue(actualExceptionText.equalsIgnoreCase("raiserror level 20"), "Test fatal errors"); + assertTrue(actualExceptionText.equalsIgnoreCase("raiserror level 20"), TestResource.getResource("R_unexpectedExceptionContent") + ": " + actualExceptionText); } } From ae8d4a133259f48b3411fc9103bc74ada531f656 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Thu, 31 May 2018 13:20:37 -0700 Subject: [PATCH 42/84] Add support for JDK 10 in both Maven and Gradle (#691) * Feature | Added support for JDK 10 in both Maven and Gradle - builds jre10 jars for the driver, replacing jre9 * JDK 10 | Merge 42 classes to base classes to reduce class redundancy. * JDK 10 | Attempt to run JDK 10 with Appveyor * Remove unwanted space * Updating Travis script to use JDK 10 * Testing without addons * Update script for Jacoco report to build 43 profile * Revert driver changes for 42 compliance - to be added in a separate PR * Revert Test class changes for 42 compliance - to be done in a separate PR * Reformatted code * Add ID to jacoco plugin execution task --- .travis.yml | 9 +- appveyor.yml | 5 +- build.gradle | 6 +- pom.xml | 122 +++++++++--------- .../connection/ConnectionWrapper43Test.java | 76 +++++++++++ 5 files changed, 145 insertions(+), 73 deletions(-) create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionWrapper43Test.java diff --git a/.travis.yml b/.travis.yml index a9a2d8f11..1a4299131 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,13 +2,8 @@ language: java jdk: - - oraclejdk9 + - oraclejdk10 -addons: - apt: - packages: - - oracle-java9-installer - services: - docker @@ -45,5 +40,5 @@ script: - docker ps -a ##Test for JDBC Specification 43 & 42 and submit coverage report. - - mvn test -B -Pbuild41 jacoco:report && bash <(curl -s https://codecov.io/bash) -cF JDBC43 + - mvn test -B -Pbuild43 jacoco:report && bash <(curl -s https://codecov.io/bash) -cF JDBC43 - mvn test -B -Pbuild42 jacoco:report && bash <(curl -s https://codecov.io/bash) -cF JDBC42 diff --git a/appveyor.yml b/appveyor.yml index cc473aded..6ab08769c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,12 +4,11 @@ init: - cmd: net start MSSQL$%SQL_Instance% environment: - JAVA_HOME: C:\Program Files\Java\jdk9 + JAVA_HOME: C:\Program Files\Java\jdk10 mssql_jdbc_test_connection_properties: jdbc:sqlserver://localhost:1433;instanceName=%SQL_Instance%;databaseName=master;username=sa;password=Password12!; - matrix: - SQL_Instance: SQL2008R2SP2 - - SQL_Instance: SQL2016 + - SQL_Instance: SQL2017 install: - ps: Write-Host 'Installing JCE with powershell' diff --git a/build.gradle b/build.gradle index b4cfce4a7..bdd3c1090 100644 --- a/build.gradle +++ b/build.gradle @@ -27,11 +27,11 @@ allprojects { if (!hasProperty('buildProfile') || (hasProperty('buildProfile') && buildProfile == "build43")){ - jreVersion = "jre9" + jreVersion = "jre10" excludedFile = 'com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java' - sourceCompatibility = 9 - targetCompatibility = 9 + sourceCompatibility = 10 + targetCompatibility = 10 } if((hasProperty('buildProfile') && buildProfile == "build42")) { diff --git a/pom.xml b/pom.xml index f1f5dd160..2696fc391 100644 --- a/pom.xml +++ b/pom.xml @@ -1,37 +1,38 @@ - 4.0.0 - + com.microsoft.sqlserver mssql-jdbc 6.5.3-SNAPSHOT jar - + Microsoft JDBC Driver for SQL Server Microsoft JDBC Driver for SQL Server. https://github.com/Microsoft/mssql-jdbc - + MIT License http://www.opensource.org/licenses/mit-license.php - + Microsoft Corporation - + Microsoft http://www.microsoft.com - + https://github.com/Microsoft/mssql-jdbc @@ -41,7 +42,7 @@ 1.2.0 5.2.0 - + com.microsoft.azure @@ -49,14 +50,14 @@ 1.0.0 true - + com.microsoft.azure adal4j 1.5.0 true - + junit @@ -157,14 +158,14 @@ - + build43 true - + - ${project.artifactId}-${project.version}.jre9-preview + ${project.artifactId}-${project.version}.jre10-preview maven-compiler-plugin @@ -173,8 +174,8 @@ **/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java - 9 - 9 + 10 + 10 @@ -219,7 +220,7 @@ - + @@ -235,13 +236,14 @@ - + org.jacoco jacoco-maven-plugin - 0.7.8 + 0.8.1 + pre-test prepare-agent @@ -256,46 +258,46 @@ - - org.apache.felix - maven-bundle-plugin - 3.4.0 - true - - - com.microsoft.sqlserver.jdbc,microsoft.sql - !microsoft.sql,* - - - - - bundle-manifest - process-classes - - manifest - - - - - + + org.apache.felix + maven-bundle-plugin + 3.4.0 + true + + + com.microsoft.sqlserver.jdbc,microsoft.sql + !microsoft.sql,* + + + + + bundle-manifest + process-classes + + manifest + + + + + org.apache.maven.plugins - maven-javadoc-plugin - 3.0.0 - - - true - - - - - attach-javadocs - - jar - - - - + maven-javadoc-plugin + 3.0.0 + + + true + + + + + attach-javadocs + + jar + + + + org.apache.maven.plugins @@ -307,7 +309,7 @@ ${skipTestTag} - + org.junit.platform @@ -326,9 +328,9 @@ outdated-dependencies.txt file:///${session.executionRootDirectory}/maven-version-rules.xml - - + + - + diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionWrapper43Test.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionWrapper43Test.java new file mode 100644 index 000000000..204673991 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionWrapper43Test.java @@ -0,0 +1,76 @@ +/* + * 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 static org.junit.jupiter.api.Assertions.assertTrue; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.SQLException; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.SQLServerConnection43; +import com.microsoft.sqlserver.testframework.AbstractTest; + +/** + * Test ConnectionWrapper43Test class + * + */ +@RunWith(JUnitPlatform.class) +public class ConnectionWrapper43Test extends AbstractTest { + static Connection connection = null; + double javaVersion = Double.parseDouble(System.getProperty("java.specification.version")); + static int major; + static int minor; + + /** + * Tests creation of SQLServerConnection43Test object + * + * @throws SQLException + */ + @Test + public void SQLServerConnection43Test() throws SQLException { + try { + if (1.8d <= javaVersion && 4 == major && 2 == minor) { + assertTrue(connection instanceof SQLServerConnection); + } + else { + assertTrue(connection instanceof SQLServerConnection43); + } + } + finally { + if (null != connection) { + connection.close(); + } + } + } + + @BeforeAll + private static void setupConnection() throws SQLException { + connection = DriverManager.getConnection(connectionString); + + DatabaseMetaData metadata = connection.getMetaData(); + major = metadata.getJDBCMajorVersion(); + minor = metadata.getJDBCMinorVersion(); + } + + @AfterAll + private static void terminateVariation() throws SQLException { + if (null != connection) { + connection.close(); + } + } + +} From 99a2e0fcab4e86389aaff85a4650097785c54e78 Mon Sep 17 00:00:00 2001 From: v-reye Date: Thu, 31 May 2018 14:36:15 -0700 Subject: [PATCH 43/84] Kerberos Constrained Delegation Impersonated Credential Expiry fix (#636) fix for automatic credential discarding * update felix to 3.5.0 * Revised implementation Decided to not dispose user created credentials at all. * Updated flag set location --- pom.xml | 4 +-- .../sqlserver/jdbc/KerbAuthentication.java | 9 ++++-- .../sqlserver/jdbc/SQLServerConnection.java | 30 ++++++++----------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/pom.xml b/pom.xml index 2696fc391..c9a7bb3ef 100644 --- a/pom.xml +++ b/pom.xml @@ -261,7 +261,7 @@ org.apache.felix maven-bundle-plugin - 3.4.0 + 3.5.0 true @@ -279,7 +279,7 @@ - + org.apache.maven.plugins maven-javadoc-plugin diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java b/src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java index 01ace7365..d3707c2fe 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java @@ -49,6 +49,7 @@ final class KerbAuthentication extends SSPIAuthentication { private final GSSManager manager = GSSManager.getInstance(); private LoginContext lc = null; + private boolean isUserCreatedCredential = false; private GSSCredential peerCredentials = null; private GSSContext peerContext = null; @@ -388,9 +389,10 @@ interface RealmValidator { KerbAuthentication(SQLServerConnection con, String address, int port, - GSSCredential ImpersonatedUserCred) throws SQLServerException { + GSSCredential ImpersonatedUserCred, Boolean isUserCreated) throws SQLServerException { this(con, address, port); peerCredentials = ImpersonatedUserCred; + this.isUserCreatedCredential = (isUserCreated == null ? false : isUserCreated); } byte[] GenerateClientContext(byte[] pin, @@ -403,8 +405,11 @@ byte[] GenerateClientContext(byte[] pin, int ReleaseClientContext() throws SQLServerException { try { - if (null != peerCredentials) + if (null != peerCredentials && !isUserCreatedCredential) { peerCredentials.dispose(); + } else if (null != peerCredentials && isUserCreatedCredential) { + peerCredentials = null; + } if (null != peerContext) peerContext.dispose(); if (null != lc) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 8d99f652d..ab7a758aa 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -753,7 +753,8 @@ static synchronized List getColumnEncryptionTrustedMasterKeyPaths(String Properties activeConnectionProperties; // the active set of connection properties private boolean integratedSecurity = SQLServerDriverBooleanProperty.INTEGRATED_SECURITY.getDefaultValue(); private AuthenticationScheme intAuthScheme = AuthenticationScheme.nativeAuthentication; - private GSSCredential ImpersonatedUserCred ; + private GSSCredential ImpersonatedUserCred; + private Boolean isUserCreatedCredential; // This is the current connect place holder this should point one of the primary or failover place holder ServerPortPlaceHolder currentConnectPlaceHolder = null; @@ -1483,8 +1484,10 @@ Connection connectInternal(Properties propsIn, if(intAuthScheme == AuthenticationScheme.javaKerberos){ sPropKey = SQLServerDriverObjectProperty.GSS_CREDENTIAL.toString(); - if(activeConnectionProperties.containsKey(sPropKey)) + if(activeConnectionProperties.containsKey(sPropKey)) { ImpersonatedUserCred = (GSSCredential) activeConnectionProperties.get(sPropKey); + isUserCreatedCredential = true; + } } sPropKey = SQLServerDriverStringProperty.AUTHENTICATION.toString(); @@ -3435,9 +3438,10 @@ final boolean doExecute() throws SQLServerException { if (integratedSecurity && AuthenticationScheme.nativeAuthentication == intAuthScheme) authentication = new AuthenticationJNI(this, currentConnectPlaceHolder.getServerName(), currentConnectPlaceHolder.getPortNumber()); if (integratedSecurity && AuthenticationScheme.javaKerberos == intAuthScheme) { - if (null != ImpersonatedUserCred) + if (null != ImpersonatedUserCred) { authentication = new KerbAuthentication(this, currentConnectPlaceHolder.getServerName(), currentConnectPlaceHolder.getPortNumber(), - ImpersonatedUserCred); + ImpersonatedUserCred, isUserCreatedCredential); + } else authentication = new KerbAuthentication(this, currentConnectPlaceHolder.getServerName(), currentConnectPlaceHolder.getPortNumber()); } @@ -3459,7 +3463,6 @@ final boolean doExecute() throws SQLServerException { // No need any further info from the server for token based authentication. So set _federatedAuthenticationRequested to true federatedAuthenticationRequested = true; } - try { sendLogon(command, authentication, fedAuthFeatureExtensionData); @@ -3473,21 +3476,14 @@ final boolean doExecute() throws SQLServerException { connectionCommand(sqlStmt, "Change Settings"); } } - } - finally { + } finally { if (integratedSecurity) { - if (null != authentication) + if (null != authentication) { authentication.ReleaseClientContext(); - authentication = null; - + authentication = null; + } if (null != ImpersonatedUserCred) { - try { - ImpersonatedUserCred.dispose(); - } - catch (GSSException e) { - if (connectionlogger.isLoggable(Level.FINER)) - connectionlogger.finer(toString() + " Release of the credentials failed GSSException: " + e); - } + ImpersonatedUserCred = null; } } } From 5c6ccd32d58a87d167e8fd661d0739c0c82e1965 Mon Sep 17 00:00:00 2001 From: rene-ye Date: Thu, 31 May 2018 16:56:32 -0700 Subject: [PATCH 44/84] changes for 6.5.3 preview release --- CHANGELOG.md | 706 +++++++++--------- README.md | 418 +++++------ build.gradle | 2 +- pom.xml | 2 +- .../sqlserver/jdbc/SQLJdbcVersion.java | 32 +- 5 files changed, 588 insertions(+), 572 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5537523d..942a582ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,345 +1,361 @@ -# Change Log -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/) - -## [6.5.2] Preview Release -### Added -- Added new connection property "cancelQueryTimeout" to cancel QueryTimeout on Connection and Statement [#674](https://github.com/Microsoft/mssql-jdbc/pull/674) - -### Fixed Issues -- Improved performance degradation while maintaining JDBC compliance with results from sp_fkeys [#677](https://github.com/Microsoft/mssql-jdbc/pull/677) -- Fixed an issue where ResultSetMetaData instances created by a ResultSet that has been closed were not persisting [#685](https://github.com/Microsoft/mssql-jdbc/pull/685) -- Fixed an issue with PreparedStatement.setBigDecimal when no scale is passed [#684](https://github.com/Microsoft/mssql-jdbc/pull/684) -- Fixed an issue with Clobs/NClobs not persisting after ResultSet/Connection closes [#682](https://github.com/Microsoft/mssql-jdbc/pull/682) - -### Changed -- Updated the samples to be usable with Eclipse directly, and updated the driver version used by the samples to 6.4.0.jre9 [#679](https://github.com/Microsoft/mssql-jdbc/pull/679) -- Updated Gradle script for building JDBC Driver [#689](https://github.com/Microsoft/mssql-jdbc/pull/689) -- Updated Maven dependencies for test suite [#676](https://github.com/Microsoft/mssql-jdbc/pull/676) -- Updated multiple Maven dependency and plugin versions [#688](https://github.com/Microsoft/mssql-jdbc/pull/688) - -## [6.5.1] Preview Release -### Added -- Test cases for Date, Time, and Datetime2 data types. [#558](https://github.com/Microsoft/mssql-jdbc/pull/558) - -### Fixed Issues -- Fixed an issue where ResultSetMetadata returned incorrect columnType for Geometry and Geography data types [#657](https://github.com/Microsoft/mssql-jdbc/pull/657) -- Fixed server side CPU Affinity problems caused by uneven connection distribution across NUMA Nodes when multiSubnetFailover is true [#662](https://github.com/Microsoft/mssql-jdbc/pull/662) -- Fixed an issue where Driver wasn't parsing TDS Packets completely to capture exceptions raised inside executed stored procedures [#664](https://github.com/Microsoft/mssql-jdbc/pull/664) -- Fixed an issue where driver throws exception when using setMaxRows() followed by query execution when SHOWPLAN_TEXT is ON [#666](https://github.com/Microsoft/mssql-jdbc/pull/666) - -### Changed -- Removed unused imports which forced users to import the ADAL4J library [#652](https://github.com/Microsoft/mssql-jdbc/pull/652) - -## [6.5.0] Preview Release -### Added -- Support for spatial datatypes [#642](https://github.com/Microsoft/mssql-jdbc/pull/642) - -### Fixed Issues -- Fixed blobs becoming unavailable when the Result Set cursor moves or the Result Set closes [#595](https://github.com/Microsoft/mssql-jdbc/pull/595) -- Fixed an issue when attempting to insert an empty or null value into an encrypted column [#632](https://github.com/Microsoft/mssql-jdbc/pull/632) -- Fixed a misleading error message thrown by the driver when a user doesn't have execute permissions [#635](https://github.com/Microsoft/mssql-jdbc/pull/635) -- Fixed statements throwing SQLServerException instead of java.sql.SQLTimeoutException when the query times out [#641](https://github.com/Microsoft/mssql-jdbc/pull/641) - -### Changed -- Unit tests now use SQLException in most cases instead of SQLServerException. - -## [6.4.0] Stable Release -### Added -- Support added for AAD Integrated Authentication with ADAL4J on Windows/Linux/Mac OS [#603](https://github.com/Microsoft/mssql-jdbc/pull/603) -- Enable Recover after MSDTC is restarted [#581](https://github.com/Microsoft/mssql-jdbc/pull/581) -- Added Version Update configuration rules to project [#541](https://github.com/Microsoft/mssql-jdbc/pull/541) -- JDK 9 Compatibility + JDBC 4.3 API support added to the driver [#601 (https://github.com/Microsoft/mssql-jdbc/pull/601) - -### Fixed Issues -- Re-introduced Retry Logic for Prepared Statement Caching implementation and remove detect change context function [#618](https://github.com/Microsoft/mssql-jdbc/pull/618) and [#620](https://github.com/Microsoft/mssql-jdbc/pull/620) -- Fixes for SonarQube Reported issues [#599](https://github.com/Microsoft/mssql-jdbc/pull/599) -- Fixes for Random Assertion Errors [#597](https://github.com/Microsoft/mssql-jdbc/pull/597) - -### Changed -- Updated Appveyor to use JDK9 building driver and running tests [#619](https://github.com/Microsoft/mssql-jdbc/pull/619) -- JDK 7 compilation support removed from the driver [#601](https://github.com/Microsoft/mssql-jdbc/pull/601) - -## [6.3.6] Preview Release -### Added -- Added support for using database name as part of the key for handle cache [#561](https://github.com/Microsoft/mssql-jdbc/pull/561) -- Updated ADAL4J version to 1.3.0 and also added it into README file [#564](https://github.com/Microsoft/mssql-jdbc/pull/564) - -### Fixed Issues -- Fixed issues with static loggers being set by every constructor invocation [#563](https://github.com/Microsoft/mssql-jdbc/pull/563) - -## [6.3.5] Preview Release -### Added -- Added handle for Account Locked Exception 18486 during login in SQLServerConnection [#522](https://github.com/Microsoft/mssql-jdbc/pull/522) - -### Fixed Issues -- Fixed the issues with Prepared Statement Metadata Caching implementation [#543](https://github.com/Microsoft/mssql-jdbc/pull/543) -- Fixed issues with static logger member in abstract class 'SQLServerClobBase' [#537](https://github.com/Microsoft/mssql-jdbc/pull/537) - -## [6.3.4] Preview Release -### Added -- Added new ThreadGroup creation to prevent IllegalThreadStateException if the underlying ThreadGroup has been destroyed. [#474](https://github.com/Microsoft/mssql-jdbc/pull/474) -- Added try-with-resources to JUnit tests [#520](https://github.com/Microsoft/mssql-jdbc/pull/520) - -### Fixed Issues -- Fixed the issue with passing parameters names that start with '@' to a CallableStatement [#495](https://github.com/Microsoft/mssql-jdbc/pull/495) -- Fixed SQLServerDataTable creation being O(n^2) issue [#514](https://github.com/Microsoft/mssql-jdbc/pull/514) - -### Changed -- Changed some manual array copying to System.arraycopy() [#500](https://github.com/Microsoft/mssql-jdbc/pull/500) -- Removed redundant toString() on String objects [#501](https://github.com/Microsoft/mssql-jdbc/pull/501) -- Replaced literals with constants [#502](https://github.com/Microsoft/mssql-jdbc/pull/502) - -## [6.3.3] Preview Release -### Added -- Added connection properties for specifying custom TrustManager [#74](https://github.com/Microsoft/mssql-jdbc/pull/74) - -### Fixed Issues -- Fixed exception thrown by getters on null columns [#488](https://github.com/Microsoft/mssql-jdbc/pull/488) -- Fixed issue with DatabaseMetaData#getImportedKeys() returns wrong value for DELETE_RULE [#490](https://github.com/Microsoft/mssql-jdbc/pull/490) -- Fixed issue with ActivityCorrelator causing a classloader leak [#465](https://github.com/Microsoft/mssql-jdbc/pull/465) - -### Changed -- Removed explicit extends Object [#469](https://github.com/Microsoft/mssql-jdbc/pull/469) -- Removed unnecessary return statements [#471](https://github.com/Microsoft/mssql-jdbc/pull/471) -- Simplified overly complex boolean expressions [#472](https://github.com/Microsoft/mssql-jdbc/pull/472) -- Replaced explicit types with <> (the diamond operator) [#420](https://github.com/Microsoft/mssql-jdbc/pull/420) - -## [6.3.2] Preview Release -### Added -- Added new connection property: sslProtocol [#422](https://github.com/Microsoft/mssql-jdbc/pull/422) -- Added "slow" tag to long running tests [#461](https://github.com/Microsoft/mssql-jdbc/pull/461) - -### Fixed Issues -- Fixed some error messages [#452](https://github.com/Microsoft/mssql-jdbc/pull/452) & [#459](https://github.com/Microsoft/mssql-jdbc/pull/459) -- Fixed statement leaks [#455](https://github.com/Microsoft/mssql-jdbc/pull/455) -- Fixed an issue regarding to loginTimeout with TLS [#456](https://github.com/Microsoft/mssql-jdbc/pull/456) -- Fixed sql_variant issue with String type [#442](https://github.com/Microsoft/mssql-jdbc/pull/442) -- Fixed issue with throwing error message for unsupported datatype [#450](https://github.com/Microsoft/mssql-jdbc/pull/450) -- Fixed issue that initial batchException was not thrown [#458](https://github.com/Microsoft/mssql-jdbc/pull/458) - -### Changed -- Changed sendStringParameterAsUnicode to impact set/update null [#445](https://github.com/Microsoft/mssql-jdbc/pull/445) -- Removed connection property: fipsProvider [#460](https://github.com/Microsoft/mssql-jdbc/pull/460) -- Replaced for and while loops with foeach loops [#421](https://github.com/Microsoft/mssql-jdbc/pull/421) -- Replaced explicit types with the diamond operator [#468](https://github.com/Microsoft/mssql-jdbc/pull/468) & [#420](https://github.com/Microsoft/mssql-jdbc/pull/420) - -## [6.3.1] Preview Release -### Added -- Added support for datetime/smallDatetime in TVP [#435](https://github.com/Microsoft/mssql-jdbc/pull/435) -- Added more Junit tests for Always Encrypted [#432](https://github.com/Microsoft/mssql-jdbc/pull/432) - -### Fixed Issues -- Fixed getString issue for uniqueIdentifier [#423](https://github.com/Microsoft/mssql-jdbc/pull/423) - -### Changed -- Skip long running tests based on Tag [#425](https://github.com/Microsoft/mssql-jdbc/pull/425) -- Removed volatile keyword [#409](https://github.com/Microsoft/mssql-jdbc/pull/409) - -## [6.3.0] Preview Release -### Added -- Added support for sql_variant datatype [#387](https://github.com/Microsoft/mssql-jdbc/pull/387) -- Added more Junit tests for Always Encrypted [#404](https://github.com/Microsoft/mssql-jdbc/pull/404) - -### Fixed Issues -- Fixed Turkey locale issue when lowercasing an "i" [#384](https://github.com/Microsoft/mssql-jdbc/pull/384) -- Fixed issue with incorrect parameter count for INSERT with subquery [#373](https://github.com/Microsoft/mssql-jdbc/pull/373) -- Fixed issue with running DDL in PreparedStatement [#372](https://github.com/Microsoft/mssql-jdbc/pull/372) -- Fixed issue with parameter metadata with whitespace characters [#371](https://github.com/Microsoft/mssql-jdbc/pull/371) -- Fixed handling of explicit boxing and unboxing [#84](https://github.com/Microsoft/mssql-jdbc/pull/84) -- Fixed metadata caching batch query issue [#393](https://github.com/Microsoft/mssql-jdbc/pull/393) -- Fixed javadoc issue for the newest maven version [#385](https://github.com/Microsoft/mssql-jdbc/pull/385) - -### Changed -- Updated ADAL4J dependency to version 1.2.0 [#392](https://github.com/Microsoft/mssql-jdbc/pull/392) -- Updated azure-keyvault dependency to version 1.0.0 [#397](https://github.com/Microsoft/mssql-jdbc/pull/397) - -## [6.2.2] Hotfix & Stable Release -### Changed -- Updated ADAL4J to version 1.2.0 and AKV to version 1.0.0 [#516](https://github.com/Microsoft/mssql-jdbc/pull/516) - -## [6.2.1] Hotfix & Stable Release -### Fixed Issues -- Fixed queries without parameters using preparedStatement [#372](https://github.com/Microsoft/mssql-jdbc/pull/372) -### Changed -- Removed metadata caching [#377](https://github.com/Microsoft/mssql-jdbc/pull/377) - -## [6.2.0] Release Candidate -### Added -- Added TVP and BulkCopy random data test for all data types with server cursor [#319](https://github.com/Microsoft/mssql-jdbc/pull/319) -- Added AE setup and test [#337](https://github.com/Microsoft/mssql-jdbc/pull/337),[328](https://github.com/Microsoft/mssql-jdbc/pull/328) -- Added validation for javadocs for every commit [#338](https://github.com/Microsoft/mssql-jdbc/pull/338) -- Added metdata caching [#345](https://github.com/Microsoft/mssql-jdbc/pull/345) -- Added caching mvn dependencies for Appveyor [#320](https://github.com/Microsoft/mssql-jdbc/pull/320) -- Added caching mvn dependencies for Travis-CI [#322](https://github.com/Microsoft/mssql-jdbc/pull/322) -- Added handle for bulkcopy exceptions [#286](https://github.com/Microsoft/mssql-jdbc/pull/286) -- Added handle for TVP exceptions [#285](https://github.com/Microsoft/mssql-jdbc/pull/285) - -### Fixed Issues -- Fixed metadata caching issue with AE on connection [#361](https://github.com/Microsoft/mssql-jdbc/pull/361) -- Fixed issue with String index out of range parameter metadata [#353](https://github.com/Microsoft/mssql-jdbc/pull/353) -- Fixed javaDocs [#354](https://github.com/Microsoft/mssql-jdbc/pull/354) -- Fixed javaDocs [#299](https://github.com/Microsoft/mssql-jdbc/pull/299) -- Performance fix from @brettwooldridge [#347](https://github.com/Microsoft/mssql-jdbc/pull/347) -- Get local host name before opening TDSChannel [#324](https://github.com/Microsoft/mssql-jdbc/pull/324) -- Fixed TVP Time issue [#317](https://github.com/Microsoft/mssql-jdbc/pull/317) -- Fixed SonarQube issues [#300](https://github.com/Microsoft/mssql-jdbc/pull/300) -- Fixed SonarQube issues [#301](https://github.com/Microsoft/mssql-jdbc/pull/301) -- Fixed random TDS invalid error [#310](https://github.com/Microsoft/mssql-jdbc/pull/310) -- Fixed password logging [#298](https://github.com/Microsoft/mssql-jdbc/pull/298) -- Fixed bulkcopy cursor issue [#270](https://github.com/Microsoft/mssql-jdbc/pull/270) - -### Changed -- Refresh Kerberos configuration [#279](https://github.com/Microsoft/mssql-jdbc/pull/279) - -## [6.1.7] Preview Release -### Added -- Added support for data type LONGVARCHAR, LONGNVARCHAR, LONGVARBINARY and SQLXML in TVP [#259](https://github.com/Microsoft/mssql-jdbc/pull/259) -- Added new connection property to accept custom JAAS configuration for Kerberos [#254](https://github.com/Microsoft/mssql-jdbc/pull/254) -- Added support for server cursor with TVP [#234](https://github.com/Microsoft/mssql-jdbc/pull/234) -- Experimental Feature: Added new connection property to support network timeout [#253](https://github.com/Microsoft/mssql-jdbc/pull/253) -- Added support to authenticate Kerberos with principal and password [#163](https://github.com/Microsoft/mssql-jdbc/pull/163) -- Added temporal types to BulkCopyCSVTestInput.csv [#262](https://github.com/Microsoft/mssql-jdbc/pull/262) -- Added automatic detection of REALM in SPN needed for Cross Domain authentication [#40](https://github.com/Microsoft/mssql-jdbc/pull/40) - -### Changed -- Updated minor semantics [#232](https://github.com/Microsoft/mssql-jdbc/pull/232) -- Cleaned up Azure Active Directory (AAD) Authentication methods [#256](https://github.com/Microsoft/mssql-jdbc/pull/256) -- Updated permission check before setting network timeout [#255](https://github.com/Microsoft/mssql-jdbc/pull/255) - -### Fixed Issues -- Turn TNIR (TransparentNetworkIPResolution) off for Azure Active Directory (AAD) Authentication and changed TNIR multipliers [#240](https://github.com/Microsoft/mssql-jdbc/pull/240) -- Wrapped ClassCastException in BulkCopy with SQLServerException [#260](https://github.com/Microsoft/mssql-jdbc/pull/260) -- Initialized the XA transaction manager for each XAResource [#257](https://github.com/Microsoft/mssql-jdbc/pull/257) -- Fixed BigDecimal scale rounding issue in BulkCopy [#230](https://github.com/Microsoft/mssql-jdbc/issues/230) -- Fixed the invalid exception thrown when stored procedure does not exist is used with TVP [#265](https://github.com/Microsoft/mssql-jdbc/pull/265) - -## [6.1.6] Preview Release -### Added -- Added constrained delegation to connection sample [#188](https://github.com/Microsoft/mssql-jdbc/pull/188) -- Added snapshot to identify nightly/dev builds [#221](https://github.com/Microsoft/mssql-jdbc/pull/221) -- Clarifying public deprecated constructors in LOBs [#226](https://github.com/Microsoft/mssql-jdbc/pull/226) -- Added OSGI Headers in MANIFEST.MF [#218](https://github.com/Microsoft/mssql-jdbc/pull/218) -- Added cause to SQLServerException [#202](https://github.com/Microsoft/mssql-jdbc/pull/202) - -### Changed -- Removed java.io.Serializable interface from SQLServerConnectionPoolProxy [#201](https://github.com/Microsoft/mssql-jdbc/pull/201) -- Refactored DROP TABLE and DROP PROCEDURE calls in test code [#222](https://github.com/Microsoft/mssql-jdbc/pull/222/files) -- Removed obsolete methods from DriverJDBCVersion [#187](https://github.com/Microsoft/mssql-jdbc/pull/187) - -### Fixed Issues -- Typos in SQLServerConnectionPoolProxy [#189](https://github.com/Microsoft/mssql-jdbc/pull/189) -- Fixed issue where exceptions are thrown if comments are in a SQL string [#157](https://github.com/Microsoft/mssql-jdbc/issues/157) -- Fixed test failures on pre-2016 servers [#215](https://github.com/Microsoft/mssql-jdbc/pull/215) -- Fixed SQLServerExceptions that are wrapped by another SQLServerException [#213](https://github.com/Microsoft/mssql-jdbc/pull/213) -- Fixed a stream isClosed error on LOBs test [#233](https://github.com/Microsoft/mssql-jdbc/pull/223) -- LOBs are fully materialised [#16](https://github.com/Microsoft/mssql-jdbc/issues/16) -- Fix precision issue in TVP [#217](https://github.com/Microsoft/mssql-jdbc/pull/217) -- Re-interrupt the current thread in order to restore the threads interrupt status [#196](https://github.com/Microsoft/mssql-jdbc/issues/196) -- Re-use parameter metadata when using Always Encrypted [#195](https://github.com/Microsoft/mssql-jdbc/issues/195) -- Improved performance for PreparedStatements through minimized server round-trips [#166](https://github.com/Microsoft/mssql-jdbc/issues/166) - -## [6.1.5] Preview Release -### Added -- Added socket timeout exception as cause[#180](https://github.com/Microsoft/mssql-jdbc/pull/180) -- Added Constrained delegation support[#178](https://github.com/Microsoft/mssql-jdbc/pull/178) -- Added junit test for Statement test[#174](https://github.com/Microsoft/mssql-jdbc/pull/174) -- Added test for statement.cancel() when MultiSubnetFailover is set to true[#173](https://github.com/Microsoft/mssql-jdbc/pull/173) -- Added tests for lobs [#168](https://github.com/Microsoft/mssql-jdbc/pull/168) -- Added badges for License, Maven Central, JavaDocs & gitter chat room [#184](https://github.com/Microsoft/mssql-jdbc/pull/184) - -### Changed -- Enabled update counts for SELECT INTO statements[#175](https://github.com/Microsoft/mssql-jdbc/pull/175) -- Use Executor service instead of thread[#162](https://github.com/Microsoft/mssql-jdbc/pull/162) -- Convert socket adaptor to socket[#160](https://github.com/Microsoft/mssql-jdbc/pull/160) - -### Fixed Issues -- Fixed local test failures [#179](https://github.com/Microsoft/mssql-jdbc/pull/179) -- Fixed random failure in BulkCopyColumnMapping test[#165](https://github.com/Microsoft/mssql-jdbc/pull/165) - -## [6.1.4] Preview Release -### Added -- Added isWrapperFor methods for MetaData classes[#94](https://github.com/Microsoft/mssql-jdbc/pull/94) -- Added Code Coverage [#136](https://github.com/Microsoft/mssql-jdbc/pull/136) -- Added TVP schema test [#137](https://github.com/Microsoft/mssql-jdbc/pull/137) -- Introduced FIPS boolean property [#135](https://github.com/Microsoft/mssql-jdbc/pull/135) -- Added unit statement test cases [#147](https://github.com/Microsoft/mssql-jdbc/pull/147) - -### Changed -- Enabled AAD Authentication with Access Token on Linux [#142](https://github.com/Microsoft/mssql-jdbc/pull/142) -- Enabled AAD Authentication with ActiveDirectoryPassword on Linux [#146](https://github.com/Microsoft/mssql-jdbc/pull/146) -- Made Azure Key Vault and Azure Active Directory Authentication Dependencies optional [#148](https://github.com/Microsoft/mssql-jdbc/pull/148) -- Getting TVP name from ParameterMetaData when using TVP with a stored procedure [#138](https://github.com/Microsoft/mssql-jdbc/pull/138) - -### Fixed Issues -- Fixed getBinaryStream issue [#133](https://github.com/Microsoft/mssql-jdbc/pull/133) -- Fixed an issue of Bulk Copy when AlwaysEncrypted is enabled on connection and destination table is not encrypted [#151](https://github.com/Microsoft/mssql-jdbc/pull/151) - - -## [6.1.3] Preview Release -### Added - - Added Binary and Varbinary types to the jUnit test framework [#119](https://github.com/Microsoft/mssql-jdbc/pull/119) - - Added BulkCopy test cases for csv [#123](https://github.com/Microsoft/mssql-jdbc/pull/123) - - Added BulkCopy ColumnMapping test cases [#127](https://github.com/Microsoft/mssql-jdbc/pull/127) - -### Changed - - Switched to clean rounding for bigDecimal [#118](https://github.com/Microsoft/mssql-jdbc/pull/118) - - Updated BVT tests to use jUnit test framework [#120](https://github.com/Microsoft/mssql-jdbc/pull/120) - - In case of socket timeout occurance, avoid connection retry [#122](https://github.com/Microsoft/mssql-jdbc/pull/122) - - Changed ant build file to skip tests [#126](https://github.com/Microsoft/mssql-jdbc/pull/126) - -### Fixed Issues - - Fixed the inconsistent coding style [#4](https://github.com/Microsoft/mssql-jdbc/issues/4) - - Fixed NullPointerException in case when SocketTimeout occurs [#65](https://github.com/Microsoft/mssql-jdbc/issues/121) - - -## [6.1.2] Preview Release -### Added - - Socket timeout implementation for both connection string and data source [#85](https://github.com/Microsoft/mssql-jdbc/pull/85) - - Query timeout API for datasource [#88](https://github.com/Microsoft/mssql-jdbc/pull/88) - - Added connection tests [#95](https://github.com/Microsoft/mssql-jdbc/pull/95) - - Added Support for FIPS enabled JVM [#97](https://github.com/Microsoft/mssql-jdbc/pull/97) - - Added additional tests for bulk copy [#110] (https://github.com/Microsoft/mssql-jdbc/pull/110) - -### Changed - - Remove redundant type casts [#63](https://github.com/Microsoft/mssql-jdbc/pull/63) - - Read SQL Server error message if status flag has DONE_ERROR set [#73](https://github.com/Microsoft/mssql-jdbc/pull/73) - - Fix a bug when the value of queryTimeout is bigger than the max value of integer [#78](https://github.com/Microsoft/mssql-jdbc/pull/78) - - Add new dependencies to gradle build script [#81](https://github.com/Microsoft/mssql-jdbc/pull/81) - - Updates to test framework [#90](https://github.com/Microsoft/mssql-jdbc/pull/90) - -### Fixed Issues - - Set the jre8 version as default [#59](https://github.com/Microsoft/mssql-jdbc/issues/59) - - Fixed exception SQL Server instance in use does not support column encryption [#65](https://github.com/Microsoft/mssql-jdbc/issues/65) - - TVP Handling is causing exception when calling SP with return value [#80](https://github.com/Microsoft/mssql-jdbc/issues/80) - - BigDecimal in TVP can incorrectly cause SQLServerException related to invalid precision or scale [#86](https://github.com/Microsoft/mssql-jdbc/issues/86) - - Fixed the connection close issue on using variant type [#91] (https://github.com/Microsoft/mssql-jdbc/issues/91) - - -## [6.1.1] Preview Release -### Added -- Java Docs [#46](https://github.com/Microsoft/mssql-jdbc/pull/46) -- Driver version number in LOGIN7 packet [#43](https://github.com/Microsoft/mssql-jdbc/pull/43) -- Travis- CI Integration [#23](https://github.com/Microsoft/mssql-jdbc/pull/23) -- Appveyor Integration [#23](https://github.com/Microsoft/mssql-jdbc/pull/23) -- Make Ms Jdbc driver more Spring friendly [#9](https://github.com/Microsoft/mssql-jdbc/pull/9) -- Implement Driver#getParentLogger [#8](https://github.com/Microsoft/mssql-jdbc/pull/8) -- Implement missing MetaData #unwrap methods [#12](https://github.com/Microsoft/mssql-jdbc/pull/12) -- Added Gradle build script [#54](https://github.com/Microsoft/mssql-jdbc/pull/54) -- Added a queryTimeout connection parameter [#45](https://github.com/Microsoft/mssql-jdbc/pull/45) -- Added Stored Procedure support for TVP [#47](https://github.com/Microsoft/mssql-jdbc/pull/47) - -### Changed -- Use StandardCharsets [#15](https://github.com/Microsoft/mssql-jdbc/pull/15) -- Use Charset throughout [#26](https://github.com/Microsoft/mssql-jdbc/pull/26) -- Upgrade azure-keyvault to 0.9.7 [#50](https://github.com/Microsoft/mssql-jdbc/pull/50) -- Avoid unnecessary calls to String copy constructor [#14](https://github.com/Microsoft/mssql-jdbc/pull/14) -- make setObject() throw a clear exception for TVP when using with result set [#48](https://github.com/Microsoft/mssql-jdbc/pull/48) -- Few clean-ups like remove wild card imports, unused imports etc. [#52](https://github.com/Microsoft/mssql-jdbc/pull/52) -- Update Maven Plugin [#55](https://github.com/Microsoft/mssql-jdbc/pull/55) - - -## [6.1.0] Stable Release -### Changed -- Open Sourced. +# Change Log +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) + +## [6.5.3] Preview Release +### Added +- Added support for JDK 10 for both Maven and Gradle [#691](https://github.com/Microsoft/mssql-jdbc/pull/691) +- Added a resource bundle to handle junit error strings [#698](https://github.com/Microsoft/mssql-jdbc/pull/698) + +### Fixed Issues +- Fixed the driver disposing user created credentials when using Kerberos Constrained Delegation [#636](https://github.com/Microsoft/mssql-jdbc/pull/636) +- Fixed an issue with HostnameInCertificate when redirected while connected to Azure [#644](https://github.com/Microsoft/mssql-jdbc/pull/644) +- Fixed an intermittent issue with Prepared Statement handle not found [#648](https://github.com/Microsoft/mssql-jdbc/pull/648) +- Fixed a conflict with JDBC Compliance where the driver was returning marked columns as SS_IS_COMPUTED instead of IS_GENERATED [#695](https://github.com/Microsoft/mssql-jdbc/pull/695) +- Fixed maven build warnings and deprecated Java API warnings [#701](https://github.com/Microsoft/mssql-jdbc/pull/701) +- Fixed some Javadoc related warnings [#702](https://github.com/Microsoft/mssql-jdbc/pull/702) + +### Changed +- The old AKV constructor was re-added into the driver and marked as deprecated. This API may be removed in future versions of the driver. [#675](https://github.com/Microsoft/mssql-jdbc/pull/675) + +## [6.5.2] Preview Release +### Added +- Added new connection property "cancelQueryTimeout" to cancel QueryTimeout on Connection and Statement [#674](https://github.com/Microsoft/mssql-jdbc/pull/674) + +### Fixed Issues +- Improved performance degradation while maintaining JDBC compliance with results from sp_fkeys [#677](https://github.com/Microsoft/mssql-jdbc/pull/677) +- Fixed an issue where ResultSetMetaData instances created by a ResultSet that has been closed were not persisting [#685](https://github.com/Microsoft/mssql-jdbc/pull/685) +- Fixed an issue with PreparedStatement.setBigDecimal when no scale is passed [#684](https://github.com/Microsoft/mssql-jdbc/pull/684) +- Fixed an issue with Clobs/NClobs not persisting after ResultSet/Connection closes [#682](https://github.com/Microsoft/mssql-jdbc/pull/682) + +### Changed +- Updated the samples to be usable with Eclipse directly, and updated the driver version used by the samples to 6.4.0.jre9 [#679](https://github.com/Microsoft/mssql-jdbc/pull/679) +- Updated Gradle script for building JDBC Driver [#689](https://github.com/Microsoft/mssql-jdbc/pull/689) +- Updated Maven dependencies for test suite [#676](https://github.com/Microsoft/mssql-jdbc/pull/676) +- Updated multiple Maven dependency and plugin versions [#688](https://github.com/Microsoft/mssql-jdbc/pull/688) + +## [6.5.1] Preview Release +### Added +- Test cases for Date, Time, and Datetime2 data types. [#558](https://github.com/Microsoft/mssql-jdbc/pull/558) + +### Fixed Issues +- Fixed an issue where ResultSetMetadata returned incorrect columnType for Geometry and Geography data types [#657](https://github.com/Microsoft/mssql-jdbc/pull/657) +- Fixed server side CPU Affinity problems caused by uneven connection distribution across NUMA Nodes when multiSubnetFailover is true [#662](https://github.com/Microsoft/mssql-jdbc/pull/662) +- Fixed an issue where Driver wasn't parsing TDS Packets completely to capture exceptions raised inside executed stored procedures [#664](https://github.com/Microsoft/mssql-jdbc/pull/664) +- Fixed an issue where driver throws exception when using setMaxRows() followed by query execution when SHOWPLAN_TEXT is ON [#666](https://github.com/Microsoft/mssql-jdbc/pull/666) + +### Changed +- Removed unused imports which forced users to import the ADAL4J library [#652](https://github.com/Microsoft/mssql-jdbc/pull/652) + +## [6.5.0] Preview Release +### Added +- Support for spatial datatypes [#642](https://github.com/Microsoft/mssql-jdbc/pull/642) + +### Fixed Issues +- Fixed blobs becoming unavailable when the Result Set cursor moves or the Result Set closes [#595](https://github.com/Microsoft/mssql-jdbc/pull/595) +- Fixed an issue when attempting to insert an empty or null value into an encrypted column [#632](https://github.com/Microsoft/mssql-jdbc/pull/632) +- Fixed a misleading error message thrown by the driver when a user doesn't have execute permissions [#635](https://github.com/Microsoft/mssql-jdbc/pull/635) +- Fixed statements throwing SQLServerException instead of java.sql.SQLTimeoutException when the query times out [#641](https://github.com/Microsoft/mssql-jdbc/pull/641) + +### Changed +- Unit tests now use SQLException in most cases instead of SQLServerException. + +## [6.4.0] Stable Release +### Added +- Support added for AAD Integrated Authentication with ADAL4J on Windows/Linux/Mac OS [#603](https://github.com/Microsoft/mssql-jdbc/pull/603) +- Enable Recover after MSDTC is restarted [#581](https://github.com/Microsoft/mssql-jdbc/pull/581) +- Added Version Update configuration rules to project [#541](https://github.com/Microsoft/mssql-jdbc/pull/541) +- JDK 9 Compatibility + JDBC 4.3 API support added to the driver [#601 (https://github.com/Microsoft/mssql-jdbc/pull/601) + +### Fixed Issues +- Re-introduced Retry Logic for Prepared Statement Caching implementation and remove detect change context function [#618](https://github.com/Microsoft/mssql-jdbc/pull/618) and [#620](https://github.com/Microsoft/mssql-jdbc/pull/620) +- Fixes for SonarQube Reported issues [#599](https://github.com/Microsoft/mssql-jdbc/pull/599) +- Fixes for Random Assertion Errors [#597](https://github.com/Microsoft/mssql-jdbc/pull/597) + +### Changed +- Updated Appveyor to use JDK9 building driver and running tests [#619](https://github.com/Microsoft/mssql-jdbc/pull/619) +- JDK 7 compilation support removed from the driver [#601](https://github.com/Microsoft/mssql-jdbc/pull/601) + +## [6.3.6] Preview Release +### Added +- Added support for using database name as part of the key for handle cache [#561](https://github.com/Microsoft/mssql-jdbc/pull/561) +- Updated ADAL4J version to 1.3.0 and also added it into README file [#564](https://github.com/Microsoft/mssql-jdbc/pull/564) + +### Fixed Issues +- Fixed issues with static loggers being set by every constructor invocation [#563](https://github.com/Microsoft/mssql-jdbc/pull/563) + +## [6.3.5] Preview Release +### Added +- Added handle for Account Locked Exception 18486 during login in SQLServerConnection [#522](https://github.com/Microsoft/mssql-jdbc/pull/522) + +### Fixed Issues +- Fixed the issues with Prepared Statement Metadata Caching implementation [#543](https://github.com/Microsoft/mssql-jdbc/pull/543) +- Fixed issues with static logger member in abstract class 'SQLServerClobBase' [#537](https://github.com/Microsoft/mssql-jdbc/pull/537) + +## [6.3.4] Preview Release +### Added +- Added new ThreadGroup creation to prevent IllegalThreadStateException if the underlying ThreadGroup has been destroyed. [#474](https://github.com/Microsoft/mssql-jdbc/pull/474) +- Added try-with-resources to JUnit tests [#520](https://github.com/Microsoft/mssql-jdbc/pull/520) + +### Fixed Issues +- Fixed the issue with passing parameters names that start with '@' to a CallableStatement [#495](https://github.com/Microsoft/mssql-jdbc/pull/495) +- Fixed SQLServerDataTable creation being O(n^2) issue [#514](https://github.com/Microsoft/mssql-jdbc/pull/514) + +### Changed +- Changed some manual array copying to System.arraycopy() [#500](https://github.com/Microsoft/mssql-jdbc/pull/500) +- Removed redundant toString() on String objects [#501](https://github.com/Microsoft/mssql-jdbc/pull/501) +- Replaced literals with constants [#502](https://github.com/Microsoft/mssql-jdbc/pull/502) + +## [6.3.3] Preview Release +### Added +- Added connection properties for specifying custom TrustManager [#74](https://github.com/Microsoft/mssql-jdbc/pull/74) + +### Fixed Issues +- Fixed exception thrown by getters on null columns [#488](https://github.com/Microsoft/mssql-jdbc/pull/488) +- Fixed issue with DatabaseMetaData#getImportedKeys() returns wrong value for DELETE_RULE [#490](https://github.com/Microsoft/mssql-jdbc/pull/490) +- Fixed issue with ActivityCorrelator causing a classloader leak [#465](https://github.com/Microsoft/mssql-jdbc/pull/465) + +### Changed +- Removed explicit extends Object [#469](https://github.com/Microsoft/mssql-jdbc/pull/469) +- Removed unnecessary return statements [#471](https://github.com/Microsoft/mssql-jdbc/pull/471) +- Simplified overly complex boolean expressions [#472](https://github.com/Microsoft/mssql-jdbc/pull/472) +- Replaced explicit types with <> (the diamond operator) [#420](https://github.com/Microsoft/mssql-jdbc/pull/420) + +## [6.3.2] Preview Release +### Added +- Added new connection property: sslProtocol [#422](https://github.com/Microsoft/mssql-jdbc/pull/422) +- Added "slow" tag to long running tests [#461](https://github.com/Microsoft/mssql-jdbc/pull/461) + +### Fixed Issues +- Fixed some error messages [#452](https://github.com/Microsoft/mssql-jdbc/pull/452) & [#459](https://github.com/Microsoft/mssql-jdbc/pull/459) +- Fixed statement leaks [#455](https://github.com/Microsoft/mssql-jdbc/pull/455) +- Fixed an issue regarding to loginTimeout with TLS [#456](https://github.com/Microsoft/mssql-jdbc/pull/456) +- Fixed sql_variant issue with String type [#442](https://github.com/Microsoft/mssql-jdbc/pull/442) +- Fixed issue with throwing error message for unsupported datatype [#450](https://github.com/Microsoft/mssql-jdbc/pull/450) +- Fixed issue that initial batchException was not thrown [#458](https://github.com/Microsoft/mssql-jdbc/pull/458) + +### Changed +- Changed sendStringParameterAsUnicode to impact set/update null [#445](https://github.com/Microsoft/mssql-jdbc/pull/445) +- Removed connection property: fipsProvider [#460](https://github.com/Microsoft/mssql-jdbc/pull/460) +- Replaced for and while loops with foeach loops [#421](https://github.com/Microsoft/mssql-jdbc/pull/421) +- Replaced explicit types with the diamond operator [#468](https://github.com/Microsoft/mssql-jdbc/pull/468) & [#420](https://github.com/Microsoft/mssql-jdbc/pull/420) + +## [6.3.1] Preview Release +### Added +- Added support for datetime/smallDatetime in TVP [#435](https://github.com/Microsoft/mssql-jdbc/pull/435) +- Added more Junit tests for Always Encrypted [#432](https://github.com/Microsoft/mssql-jdbc/pull/432) + +### Fixed Issues +- Fixed getString issue for uniqueIdentifier [#423](https://github.com/Microsoft/mssql-jdbc/pull/423) + +### Changed +- Skip long running tests based on Tag [#425](https://github.com/Microsoft/mssql-jdbc/pull/425) +- Removed volatile keyword [#409](https://github.com/Microsoft/mssql-jdbc/pull/409) + +## [6.3.0] Preview Release +### Added +- Added support for sql_variant datatype [#387](https://github.com/Microsoft/mssql-jdbc/pull/387) +- Added more Junit tests for Always Encrypted [#404](https://github.com/Microsoft/mssql-jdbc/pull/404) + +### Fixed Issues +- Fixed Turkey locale issue when lowercasing an "i" [#384](https://github.com/Microsoft/mssql-jdbc/pull/384) +- Fixed issue with incorrect parameter count for INSERT with subquery [#373](https://github.com/Microsoft/mssql-jdbc/pull/373) +- Fixed issue with running DDL in PreparedStatement [#372](https://github.com/Microsoft/mssql-jdbc/pull/372) +- Fixed issue with parameter metadata with whitespace characters [#371](https://github.com/Microsoft/mssql-jdbc/pull/371) +- Fixed handling of explicit boxing and unboxing [#84](https://github.com/Microsoft/mssql-jdbc/pull/84) +- Fixed metadata caching batch query issue [#393](https://github.com/Microsoft/mssql-jdbc/pull/393) +- Fixed javadoc issue for the newest maven version [#385](https://github.com/Microsoft/mssql-jdbc/pull/385) + +### Changed +- Updated ADAL4J dependency to version 1.2.0 [#392](https://github.com/Microsoft/mssql-jdbc/pull/392) +- Updated azure-keyvault dependency to version 1.0.0 [#397](https://github.com/Microsoft/mssql-jdbc/pull/397) + +## [6.2.2] Hotfix & Stable Release +### Changed +- Updated ADAL4J to version 1.2.0 and AKV to version 1.0.0 [#516](https://github.com/Microsoft/mssql-jdbc/pull/516) + +## [6.2.1] Hotfix & Stable Release +### Fixed Issues +- Fixed queries without parameters using preparedStatement [#372](https://github.com/Microsoft/mssql-jdbc/pull/372) +### Changed +- Removed metadata caching [#377](https://github.com/Microsoft/mssql-jdbc/pull/377) + +## [6.2.0] Release Candidate +### Added +- Added TVP and BulkCopy random data test for all data types with server cursor [#319](https://github.com/Microsoft/mssql-jdbc/pull/319) +- Added AE setup and test [#337](https://github.com/Microsoft/mssql-jdbc/pull/337),[328](https://github.com/Microsoft/mssql-jdbc/pull/328) +- Added validation for javadocs for every commit [#338](https://github.com/Microsoft/mssql-jdbc/pull/338) +- Added metdata caching [#345](https://github.com/Microsoft/mssql-jdbc/pull/345) +- Added caching mvn dependencies for Appveyor [#320](https://github.com/Microsoft/mssql-jdbc/pull/320) +- Added caching mvn dependencies for Travis-CI [#322](https://github.com/Microsoft/mssql-jdbc/pull/322) +- Added handle for bulkcopy exceptions [#286](https://github.com/Microsoft/mssql-jdbc/pull/286) +- Added handle for TVP exceptions [#285](https://github.com/Microsoft/mssql-jdbc/pull/285) + +### Fixed Issues +- Fixed metadata caching issue with AE on connection [#361](https://github.com/Microsoft/mssql-jdbc/pull/361) +- Fixed issue with String index out of range parameter metadata [#353](https://github.com/Microsoft/mssql-jdbc/pull/353) +- Fixed javaDocs [#354](https://github.com/Microsoft/mssql-jdbc/pull/354) +- Fixed javaDocs [#299](https://github.com/Microsoft/mssql-jdbc/pull/299) +- Performance fix from @brettwooldridge [#347](https://github.com/Microsoft/mssql-jdbc/pull/347) +- Get local host name before opening TDSChannel [#324](https://github.com/Microsoft/mssql-jdbc/pull/324) +- Fixed TVP Time issue [#317](https://github.com/Microsoft/mssql-jdbc/pull/317) +- Fixed SonarQube issues [#300](https://github.com/Microsoft/mssql-jdbc/pull/300) +- Fixed SonarQube issues [#301](https://github.com/Microsoft/mssql-jdbc/pull/301) +- Fixed random TDS invalid error [#310](https://github.com/Microsoft/mssql-jdbc/pull/310) +- Fixed password logging [#298](https://github.com/Microsoft/mssql-jdbc/pull/298) +- Fixed bulkcopy cursor issue [#270](https://github.com/Microsoft/mssql-jdbc/pull/270) + +### Changed +- Refresh Kerberos configuration [#279](https://github.com/Microsoft/mssql-jdbc/pull/279) + +## [6.1.7] Preview Release +### Added +- Added support for data type LONGVARCHAR, LONGNVARCHAR, LONGVARBINARY and SQLXML in TVP [#259](https://github.com/Microsoft/mssql-jdbc/pull/259) +- Added new connection property to accept custom JAAS configuration for Kerberos [#254](https://github.com/Microsoft/mssql-jdbc/pull/254) +- Added support for server cursor with TVP [#234](https://github.com/Microsoft/mssql-jdbc/pull/234) +- Experimental Feature: Added new connection property to support network timeout [#253](https://github.com/Microsoft/mssql-jdbc/pull/253) +- Added support to authenticate Kerberos with principal and password [#163](https://github.com/Microsoft/mssql-jdbc/pull/163) +- Added temporal types to BulkCopyCSVTestInput.csv [#262](https://github.com/Microsoft/mssql-jdbc/pull/262) +- Added automatic detection of REALM in SPN needed for Cross Domain authentication [#40](https://github.com/Microsoft/mssql-jdbc/pull/40) + +### Changed +- Updated minor semantics [#232](https://github.com/Microsoft/mssql-jdbc/pull/232) +- Cleaned up Azure Active Directory (AAD) Authentication methods [#256](https://github.com/Microsoft/mssql-jdbc/pull/256) +- Updated permission check before setting network timeout [#255](https://github.com/Microsoft/mssql-jdbc/pull/255) + +### Fixed Issues +- Turn TNIR (TransparentNetworkIPResolution) off for Azure Active Directory (AAD) Authentication and changed TNIR multipliers [#240](https://github.com/Microsoft/mssql-jdbc/pull/240) +- Wrapped ClassCastException in BulkCopy with SQLServerException [#260](https://github.com/Microsoft/mssql-jdbc/pull/260) +- Initialized the XA transaction manager for each XAResource [#257](https://github.com/Microsoft/mssql-jdbc/pull/257) +- Fixed BigDecimal scale rounding issue in BulkCopy [#230](https://github.com/Microsoft/mssql-jdbc/issues/230) +- Fixed the invalid exception thrown when stored procedure does not exist is used with TVP [#265](https://github.com/Microsoft/mssql-jdbc/pull/265) + +## [6.1.6] Preview Release +### Added +- Added constrained delegation to connection sample [#188](https://github.com/Microsoft/mssql-jdbc/pull/188) +- Added snapshot to identify nightly/dev builds [#221](https://github.com/Microsoft/mssql-jdbc/pull/221) +- Clarifying public deprecated constructors in LOBs [#226](https://github.com/Microsoft/mssql-jdbc/pull/226) +- Added OSGI Headers in MANIFEST.MF [#218](https://github.com/Microsoft/mssql-jdbc/pull/218) +- Added cause to SQLServerException [#202](https://github.com/Microsoft/mssql-jdbc/pull/202) + +### Changed +- Removed java.io.Serializable interface from SQLServerConnectionPoolProxy [#201](https://github.com/Microsoft/mssql-jdbc/pull/201) +- Refactored DROP TABLE and DROP PROCEDURE calls in test code [#222](https://github.com/Microsoft/mssql-jdbc/pull/222/files) +- Removed obsolete methods from DriverJDBCVersion [#187](https://github.com/Microsoft/mssql-jdbc/pull/187) + +### Fixed Issues +- Typos in SQLServerConnectionPoolProxy [#189](https://github.com/Microsoft/mssql-jdbc/pull/189) +- Fixed issue where exceptions are thrown if comments are in a SQL string [#157](https://github.com/Microsoft/mssql-jdbc/issues/157) +- Fixed test failures on pre-2016 servers [#215](https://github.com/Microsoft/mssql-jdbc/pull/215) +- Fixed SQLServerExceptions that are wrapped by another SQLServerException [#213](https://github.com/Microsoft/mssql-jdbc/pull/213) +- Fixed a stream isClosed error on LOBs test [#233](https://github.com/Microsoft/mssql-jdbc/pull/223) +- LOBs are fully materialised [#16](https://github.com/Microsoft/mssql-jdbc/issues/16) +- Fix precision issue in TVP [#217](https://github.com/Microsoft/mssql-jdbc/pull/217) +- Re-interrupt the current thread in order to restore the threads interrupt status [#196](https://github.com/Microsoft/mssql-jdbc/issues/196) +- Re-use parameter metadata when using Always Encrypted [#195](https://github.com/Microsoft/mssql-jdbc/issues/195) +- Improved performance for PreparedStatements through minimized server round-trips [#166](https://github.com/Microsoft/mssql-jdbc/issues/166) + +## [6.1.5] Preview Release +### Added +- Added socket timeout exception as cause[#180](https://github.com/Microsoft/mssql-jdbc/pull/180) +- Added Constrained delegation support[#178](https://github.com/Microsoft/mssql-jdbc/pull/178) +- Added junit test for Statement test[#174](https://github.com/Microsoft/mssql-jdbc/pull/174) +- Added test for statement.cancel() when MultiSubnetFailover is set to true[#173](https://github.com/Microsoft/mssql-jdbc/pull/173) +- Added tests for lobs [#168](https://github.com/Microsoft/mssql-jdbc/pull/168) +- Added badges for License, Maven Central, JavaDocs & gitter chat room [#184](https://github.com/Microsoft/mssql-jdbc/pull/184) + +### Changed +- Enabled update counts for SELECT INTO statements[#175](https://github.com/Microsoft/mssql-jdbc/pull/175) +- Use Executor service instead of thread[#162](https://github.com/Microsoft/mssql-jdbc/pull/162) +- Convert socket adaptor to socket[#160](https://github.com/Microsoft/mssql-jdbc/pull/160) + +### Fixed Issues +- Fixed local test failures [#179](https://github.com/Microsoft/mssql-jdbc/pull/179) +- Fixed random failure in BulkCopyColumnMapping test[#165](https://github.com/Microsoft/mssql-jdbc/pull/165) + +## [6.1.4] Preview Release +### Added +- Added isWrapperFor methods for MetaData classes[#94](https://github.com/Microsoft/mssql-jdbc/pull/94) +- Added Code Coverage [#136](https://github.com/Microsoft/mssql-jdbc/pull/136) +- Added TVP schema test [#137](https://github.com/Microsoft/mssql-jdbc/pull/137) +- Introduced FIPS boolean property [#135](https://github.com/Microsoft/mssql-jdbc/pull/135) +- Added unit statement test cases [#147](https://github.com/Microsoft/mssql-jdbc/pull/147) + +### Changed +- Enabled AAD Authentication with Access Token on Linux [#142](https://github.com/Microsoft/mssql-jdbc/pull/142) +- Enabled AAD Authentication with ActiveDirectoryPassword on Linux [#146](https://github.com/Microsoft/mssql-jdbc/pull/146) +- Made Azure Key Vault and Azure Active Directory Authentication Dependencies optional [#148](https://github.com/Microsoft/mssql-jdbc/pull/148) +- Getting TVP name from ParameterMetaData when using TVP with a stored procedure [#138](https://github.com/Microsoft/mssql-jdbc/pull/138) + +### Fixed Issues +- Fixed getBinaryStream issue [#133](https://github.com/Microsoft/mssql-jdbc/pull/133) +- Fixed an issue of Bulk Copy when AlwaysEncrypted is enabled on connection and destination table is not encrypted [#151](https://github.com/Microsoft/mssql-jdbc/pull/151) + + +## [6.1.3] Preview Release +### Added + - Added Binary and Varbinary types to the jUnit test framework [#119](https://github.com/Microsoft/mssql-jdbc/pull/119) + - Added BulkCopy test cases for csv [#123](https://github.com/Microsoft/mssql-jdbc/pull/123) + - Added BulkCopy ColumnMapping test cases [#127](https://github.com/Microsoft/mssql-jdbc/pull/127) + +### Changed + - Switched to clean rounding for bigDecimal [#118](https://github.com/Microsoft/mssql-jdbc/pull/118) + - Updated BVT tests to use jUnit test framework [#120](https://github.com/Microsoft/mssql-jdbc/pull/120) + - In case of socket timeout occurance, avoid connection retry [#122](https://github.com/Microsoft/mssql-jdbc/pull/122) + - Changed ant build file to skip tests [#126](https://github.com/Microsoft/mssql-jdbc/pull/126) + +### Fixed Issues + - Fixed the inconsistent coding style [#4](https://github.com/Microsoft/mssql-jdbc/issues/4) + - Fixed NullPointerException in case when SocketTimeout occurs [#65](https://github.com/Microsoft/mssql-jdbc/issues/121) + + +## [6.1.2] Preview Release +### Added + - Socket timeout implementation for both connection string and data source [#85](https://github.com/Microsoft/mssql-jdbc/pull/85) + - Query timeout API for datasource [#88](https://github.com/Microsoft/mssql-jdbc/pull/88) + - Added connection tests [#95](https://github.com/Microsoft/mssql-jdbc/pull/95) + - Added Support for FIPS enabled JVM [#97](https://github.com/Microsoft/mssql-jdbc/pull/97) + - Added additional tests for bulk copy [#110] (https://github.com/Microsoft/mssql-jdbc/pull/110) + +### Changed + - Remove redundant type casts [#63](https://github.com/Microsoft/mssql-jdbc/pull/63) + - Read SQL Server error message if status flag has DONE_ERROR set [#73](https://github.com/Microsoft/mssql-jdbc/pull/73) + - Fix a bug when the value of queryTimeout is bigger than the max value of integer [#78](https://github.com/Microsoft/mssql-jdbc/pull/78) + - Add new dependencies to gradle build script [#81](https://github.com/Microsoft/mssql-jdbc/pull/81) + - Updates to test framework [#90](https://github.com/Microsoft/mssql-jdbc/pull/90) + +### Fixed Issues + - Set the jre8 version as default [#59](https://github.com/Microsoft/mssql-jdbc/issues/59) + - Fixed exception SQL Server instance in use does not support column encryption [#65](https://github.com/Microsoft/mssql-jdbc/issues/65) + - TVP Handling is causing exception when calling SP with return value [#80](https://github.com/Microsoft/mssql-jdbc/issues/80) + - BigDecimal in TVP can incorrectly cause SQLServerException related to invalid precision or scale [#86](https://github.com/Microsoft/mssql-jdbc/issues/86) + - Fixed the connection close issue on using variant type [#91] (https://github.com/Microsoft/mssql-jdbc/issues/91) + + +## [6.1.1] Preview Release +### Added +- Java Docs [#46](https://github.com/Microsoft/mssql-jdbc/pull/46) +- Driver version number in LOGIN7 packet [#43](https://github.com/Microsoft/mssql-jdbc/pull/43) +- Travis- CI Integration [#23](https://github.com/Microsoft/mssql-jdbc/pull/23) +- Appveyor Integration [#23](https://github.com/Microsoft/mssql-jdbc/pull/23) +- Make Ms Jdbc driver more Spring friendly [#9](https://github.com/Microsoft/mssql-jdbc/pull/9) +- Implement Driver#getParentLogger [#8](https://github.com/Microsoft/mssql-jdbc/pull/8) +- Implement missing MetaData #unwrap methods [#12](https://github.com/Microsoft/mssql-jdbc/pull/12) +- Added Gradle build script [#54](https://github.com/Microsoft/mssql-jdbc/pull/54) +- Added a queryTimeout connection parameter [#45](https://github.com/Microsoft/mssql-jdbc/pull/45) +- Added Stored Procedure support for TVP [#47](https://github.com/Microsoft/mssql-jdbc/pull/47) + +### Changed +- Use StandardCharsets [#15](https://github.com/Microsoft/mssql-jdbc/pull/15) +- Use Charset throughout [#26](https://github.com/Microsoft/mssql-jdbc/pull/26) +- Upgrade azure-keyvault to 0.9.7 [#50](https://github.com/Microsoft/mssql-jdbc/pull/50) +- Avoid unnecessary calls to String copy constructor [#14](https://github.com/Microsoft/mssql-jdbc/pull/14) +- make setObject() throw a clear exception for TVP when using with result set [#48](https://github.com/Microsoft/mssql-jdbc/pull/48) +- Few clean-ups like remove wild card imports, unused imports etc. [#52](https://github.com/Microsoft/mssql-jdbc/pull/52) +- Update Maven Plugin [#55](https://github.com/Microsoft/mssql-jdbc/pull/55) + + +## [6.1.0] Stable Release +### Changed +- Open Sourced. diff --git a/README.md b/README.md index b80bdb198..346abe951 100644 --- a/README.md +++ b/README.md @@ -1,209 +1,209 @@ -[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/Microsoft/mssql-jdbc/master/LICENSE) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.microsoft.sqlserver/mssql-jdbc/badge.svg)](http://mvnrepository.com/artifact/com.microsoft.sqlserver/mssql-jdbc) -[![codecov.io](http://codecov.io/github/Microsoft/mssql-jdbc/coverage.svg?branch=master)](http://codecov.io/github/Microsoft/mssql-jdbc?branch=master) -[![Javadocs](http://javadoc.io/badge/com.microsoft.sqlserver/mssql-jdbc.svg)](http://javadoc.io/doc/com.microsoft.sqlserver/mssql-jdbc) -[![Gitter](https://img.shields.io/gitter/room/badges/shields.svg)](https://gitter.im/Microsoft/mssql-developers) -
-# Microsoft JDBC Driver for SQL Server - -Welcome to the Microsoft JDBC Driver for SQL Server project! - -The Microsoft JDBC Driver for SQL Server is a Type 4 JDBC driver that provides database connectivity through the standard JDBC application program interfaces (APIs) available in the Java Platform, Enterprise Editions. The Driver provides access to Microsoft SQL Server and Azure SQL Database from any Java application, application server, or Java-enabled applet. - -We hope you enjoy using the Microsoft JDBC Driver for SQL Server. - -SQL Server Team - -## Take our survey - -Let us know how you think we're doing. - - - -## Status of Most Recent Builds -| AppVeyor (Windows) | Travis CI (Linux) | -|--------------------------|--------------------------| -| [![AppVeyor ](https://ci.appveyor.com/api/projects/status/o6fjg16678ol64d3?svg=true "Windows")](https://ci.appveyor.com/project/Microsoft-JDBC/mssql-jdbc) | [![Travis CI](https://travis-ci.org/Microsoft/mssql-jdbc.svg? "Linux")](https://travis-ci.org/Microsoft/mssql-jdbc ) |vg? "Linux" - -## Announcements -What's coming next? We will look into adding a more comprehensive set of tests, improving our javadocs, and start developing the next set of features. - -## Get Started -* [**Ubuntu + SQL Server + Java**](https://www.microsoft.com/en-us/sql-server/developer-get-started/java/ubuntu) -* [**Red Hat + SQL Server + Java**](https://www.microsoft.com/en-us/sql-server/developer-get-started/java/rhel) -* [**Mac + SQL Server + Java**](https://www.microsoft.com/en-us/sql-server/developer-get-started/java/mac) -* [**Windows + SQL Server + Java**](https://www.microsoft.com/en-us/sql-server/developer-get-started/java/windows) - -## Build -### Prerequisites -* Java 9 -* [Maven](http://maven.apache.org/download.cgi) -* An instance of SQL Server or Azure SQL Database that you can connect to. - -### Build the JAR files -Maven builds automatically trigger a set of verification tests to run. For these tests to pass, you will first need to add an environment variable in your system called `mssql_jdbc_test_connection_properties` to provide the [correct connection properties](https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url) for your SQL Server or Azure SQL Database instance. - -To build the jar files, you must use Java 9 with Maven. You can choose to build a JDBC 4.3 compliant jar file (for use with JRE 9) and/or a JDBC 4.2 compliant jar file (for use with JRE 8). - -* Maven: - 1. If you have not already done so, add the environment variable `mssql_jdbc_test_connection_properties` in your system with the connection properties for your SQL Server or SQL DB instance. - 2. Run one of the commands below to build a JDBC 4.3 compliant jar or JDBC 4.2 compliant jar in the \target directory. - * Run `mvn install -Pbuild43`. This creates JDBC 4.3 compliant jar in \target directory - * Run `mvn install -Pbuild42`. This creates JDBC 4.2 compliant jar in \target directory - -* Gradle: - 1. If you have not already done so, add the environment variable `mssql_jdbc_test_connection_properties` in your system with the connection properties for your SQL Server or SQL DB instance. - 2. Run one of the commands below to build a JDBC 4.3 compliant jar or JDBC 4.2 compliant jar in the \build\libs directory. - * Run `gradle build -PbuildProfile=build43`. This creates JDBC 4.3 compliant jar in \build\libs directory - * Run `gradle build -PbuildProfile=build42`. This creates JDBC 4.2 compliant jar in \build\libs directory - -## Resources - -### Documentation -API reference documentation is available in [Javadocs](https://aka.ms/jdbcjavadocs). - -This driver is documented on [Microsoft's Documentation web site](https://docs.microsoft.com/en-us/sql/connect/jdbc/getting-started-with-the-jdbc-driver). - -### Sample Code -For samples, please see the src\sample directory. - -### Download the DLLs -For some features (e.g. Integrated Authentication and Distributed Transactions), you may need to use the `sqljdbc_xa` and `sqljdbc_auth` DLLs. They can be downloaded from the [Microsoft Download Center](https://go.microsoft.com/fwlink/?linkid=868287) - -### Download the driver -Don't want to compile anything? - -We're now on the Maven Central Repository. Add the following to your POM file to get the most stable release: -```xml - - com.microsoft.sqlserver - mssql-jdbc - 6.4.0.jre9 - -``` -The driver can be downloaded from the [Microsoft Download Center](https://go.microsoft.com/fwlink/?linkid=868287). - -To get the latest preview version of the driver, add the following to your POM file: -```xml - - com.microsoft.sqlserver - mssql-jdbc - 6.5.2.jre9-preview - -``` - - - -## Dependencies -This project has following dependencies: - -Compile Time: - - `azure-keyvault` : Azure Key Vault Provider for Always Encrypted Azure Key Vault feature (optional) - - `adal4j` : Azure ActiveDirectory Library for Java for Azure Active Directory Authentication feature and Azure Key Vault feature (optional) - -Test Time: - - `junit:jar` : For Unit Test cases. - -### Dependency Tree -One can see all dependencies including Transitive Dependency by executing following command. -``` -mvn dependency:tree -``` - -### Azure Key Vault and Azure Active Directory Authentication Dependencies -Projects that require either of the two features need to explicitly declare the dependency in their pom file. - -***For Example:*** If you are using *Azure Active Directory Authentication feature* then you need to redeclare *adal4j* dependency in your project's pom file. Please see the following snippet: -```xml - - com.microsoft.sqlserver - mssql-jdbc - 6.5.2.jre9-preview - compile - - - - com.microsoft.azure - adal4j - 1.5.0 - -``` - -***For Example:*** If you are using *Azure Key Vault feature* then you need to redeclare *azure-keyvault* dependency and *adal4j* dependency in your project's pom file. Please see the following snippet: -```xml - - com.microsoft.sqlserver - mssql-jdbc - 6.5.2.jre9-preview - compile - - - - com.microsoft.azure - adal4j - 1.5.0 - - - - com.microsoft.azure - azure-keyvault - 1.0.0 - -``` -***Please note*** as of the v6.2.2, the way to construct a `SQLServerColumnEncryptionAzureKeyVaultProvider` object has changed. Please refer to this [Wiki](https://github.com/Microsoft/mssql-jdbc/wiki/New-Constructor-Definition-for-SQLServerColumnEncryptionAzureKeyVaultProvider-after-6.2.2-Release) page for more information. - -## Guidelines for Creating Pull Requests -We love contributions from the community. To help improve the quality of our code, we encourage you to use the mssql-jdbc_formatter.xml formatter provided on all pull requests. - -Thank you! - -## Guidelines for Reporting Issues -We appreciate you taking the time to test the driver, provide feedback and report any issues. It would be extremely helpful if you: - -- Report each issue as a new issue (but check first if it's already been reported) -- Try to be detailed in your report. Useful information for good bug reports include: - * What you are seeing and what the expected behaviour is - * Which jar file? - * Environment details: e.g. Java version, client operating system? - * Table schema (for some issues the data types make a big difference!) - * Any other relevant information you want to share -- Try to include a Java sample demonstrating the isolated problem. - -Thank you! - -### Reporting security issues and security bugs -Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) [secure@microsoft.com](mailto:secure@microsoft.com). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the [Security TechCenter](https://technet.microsoft.com/en-us/security/ff852094.aspx). - -## Release roadmap and standards -Our goal is to release regular updates which improve the driver and bring new features to users. Stable, production quality releases happen twice a year, targeting the first and third quarters of the calendar year. They are tested against a comprehensive matrix of supported operating systems, Java versions, and SQL Server versions. Stable releases are accompanied by additional localized packages, which are available on the Microsoft website. - -Preview releases happen approximately monthly between stable releases. This gives users an opportunity to try out new features and provide feedback on them before they go into stable releases. Preview releases also include frequent bug fixes for customers to verify without having to wait for a stable release. Preview releases are only available in English. While they are tested, preview releases do not necessarily go through the same rigorous, full test matrix and review process as stable releases. - -You can see what is going into a future release by monitoring [Milestones](https://github.com/Microsoft/mssql-jdbc/milestones) in the repository. - -### Versioning convention -Starting with 6.0, stable versions have an even minor version. For example, 6.0, 6.2, 6.4. Preview versions have an odd minor version. For example, 6.1, 6.3, 6.5. - -## Contributors -Special thanks to everyone who has contributed to the project. - -Up-to-date list of contributors: https://github.com/Microsoft/mssql-jdbc/graphs/contributors - -- marschall (Philippe Marschall) -- pierresouchay (Pierre Souchay) -- gordthompson (Gord Thompson) -- gstojsic -- cosmofrit -- JamieMagee (Jamie Magee) -- mfriesen (Mike Friesen) -- tonytamwk -- sehrope (Sehrope Sarkuni) -- jacobovazquez -- brettwooldridge (Brett Wooldridge) - -## License -The Microsoft JDBC Driver for SQL Server is licensed under the MIT license. See the [LICENSE](https://github.com/Microsoft/mssql-jdbc/blob/master/LICENSE) file for more details. - -## Code of conduct -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. - +[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/Microsoft/mssql-jdbc/master/LICENSE) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.microsoft.sqlserver/mssql-jdbc/badge.svg)](http://mvnrepository.com/artifact/com.microsoft.sqlserver/mssql-jdbc) +[![codecov.io](http://codecov.io/github/Microsoft/mssql-jdbc/coverage.svg?branch=master)](http://codecov.io/github/Microsoft/mssql-jdbc?branch=master) +[![Javadocs](http://javadoc.io/badge/com.microsoft.sqlserver/mssql-jdbc.svg)](http://javadoc.io/doc/com.microsoft.sqlserver/mssql-jdbc) +[![Gitter](https://img.shields.io/gitter/room/badges/shields.svg)](https://gitter.im/Microsoft/mssql-developers) +
+# Microsoft JDBC Driver for SQL Server + +Welcome to the Microsoft JDBC Driver for SQL Server project! + +The Microsoft JDBC Driver for SQL Server is a Type 4 JDBC driver that provides database connectivity through the standard JDBC application program interfaces (APIs) available in the Java Platform, Enterprise Editions. The Driver provides access to Microsoft SQL Server and Azure SQL Database from any Java application, application server, or Java-enabled applet. + +We hope you enjoy using the Microsoft JDBC Driver for SQL Server. + +SQL Server Team + +## Take our survey + +Let us know how you think we're doing. + + + +## Status of Most Recent Builds +| AppVeyor (Windows) | Travis CI (Linux) | +|--------------------------|--------------------------| +| [![AppVeyor ](https://ci.appveyor.com/api/projects/status/o6fjg16678ol64d3?svg=true "Windows")](https://ci.appveyor.com/project/Microsoft-JDBC/mssql-jdbc) | [![Travis CI](https://travis-ci.org/Microsoft/mssql-jdbc.svg? "Linux")](https://travis-ci.org/Microsoft/mssql-jdbc ) |vg? "Linux" + +## Announcements +What's coming next? We will look into adding a more comprehensive set of tests, improving our javadocs, and start developing the next set of features. + +## Get Started +* [**Ubuntu + SQL Server + Java**](https://www.microsoft.com/en-us/sql-server/developer-get-started/java/ubuntu) +* [**Red Hat + SQL Server + Java**](https://www.microsoft.com/en-us/sql-server/developer-get-started/java/rhel) +* [**Mac + SQL Server + Java**](https://www.microsoft.com/en-us/sql-server/developer-get-started/java/mac) +* [**Windows + SQL Server + Java**](https://www.microsoft.com/en-us/sql-server/developer-get-started/java/windows) + +## Build +### Prerequisites +* Java 9 +* [Maven](http://maven.apache.org/download.cgi) +* An instance of SQL Server or Azure SQL Database that you can connect to. + +### Build the JAR files +Maven builds automatically trigger a set of verification tests to run. For these tests to pass, you will first need to add an environment variable in your system called `mssql_jdbc_test_connection_properties` to provide the [correct connection properties](https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url) for your SQL Server or Azure SQL Database instance. + +To build the jar files, you must use Java 9 with Maven. You can choose to build a JDBC 4.3 compliant jar file (for use with JRE 9) and/or a JDBC 4.2 compliant jar file (for use with JRE 8). + +* Maven: + 1. If you have not already done so, add the environment variable `mssql_jdbc_test_connection_properties` in your system with the connection properties for your SQL Server or SQL DB instance. + 2. Run one of the commands below to build a JDBC 4.3 compliant jar or JDBC 4.2 compliant jar in the \target directory. + * Run `mvn install -Pbuild43`. This creates JDBC 4.3 compliant jar in \target directory + * Run `mvn install -Pbuild42`. This creates JDBC 4.2 compliant jar in \target directory + +* Gradle: + 1. If you have not already done so, add the environment variable `mssql_jdbc_test_connection_properties` in your system with the connection properties for your SQL Server or SQL DB instance. + 2. Run one of the commands below to build a JDBC 4.3 compliant jar or JDBC 4.2 compliant jar in the \build\libs directory. + * Run `gradle build -PbuildProfile=build43`. This creates JDBC 4.3 compliant jar in \build\libs directory + * Run `gradle build -PbuildProfile=build42`. This creates JDBC 4.2 compliant jar in \build\libs directory + +## Resources + +### Documentation +API reference documentation is available in [Javadocs](https://aka.ms/jdbcjavadocs). + +This driver is documented on [Microsoft's Documentation web site](https://docs.microsoft.com/en-us/sql/connect/jdbc/getting-started-with-the-jdbc-driver). + +### Sample Code +For samples, please see the src\sample directory. + +### Download the DLLs +For some features (e.g. Integrated Authentication and Distributed Transactions), you may need to use the `sqljdbc_xa` and `sqljdbc_auth` DLLs. They can be downloaded from the [Microsoft Download Center](https://go.microsoft.com/fwlink/?linkid=868287) + +### Download the driver +Don't want to compile anything? + +We're now on the Maven Central Repository. Add the following to your POM file to get the most stable release: +```xml + + com.microsoft.sqlserver + mssql-jdbc + 6.4.0.jre9 + +``` +The driver can be downloaded from the [Microsoft Download Center](https://go.microsoft.com/fwlink/?linkid=868287). + +To get the latest preview version of the driver, add the following to your POM file: +```xml + + com.microsoft.sqlserver + mssql-jdbc + 6.5.3.jre9-preview + +``` + + + +## Dependencies +This project has following dependencies: + +Compile Time: + - `azure-keyvault` : Azure Key Vault Provider for Always Encrypted Azure Key Vault feature (optional) + - `adal4j` : Azure ActiveDirectory Library for Java for Azure Active Directory Authentication feature and Azure Key Vault feature (optional) + +Test Time: + - `junit:jar` : For Unit Test cases. + +### Dependency Tree +One can see all dependencies including Transitive Dependency by executing following command. +``` +mvn dependency:tree +``` + +### Azure Key Vault and Azure Active Directory Authentication Dependencies +Projects that require either of the two features need to explicitly declare the dependency in their pom file. + +***For Example:*** If you are using *Azure Active Directory Authentication feature* then you need to redeclare *adal4j* dependency in your project's pom file. Please see the following snippet: +```xml + + com.microsoft.sqlserver + mssql-jdbc + 6.5.3.jre9-preview + compile + + + + com.microsoft.azure + adal4j + 1.5.0 + +``` + +***For Example:*** If you are using *Azure Key Vault feature* then you need to redeclare *azure-keyvault* dependency and *adal4j* dependency in your project's pom file. Please see the following snippet: +```xml + + com.microsoft.sqlserver + mssql-jdbc + 6.5.3.jre9-preview + compile + + + + com.microsoft.azure + adal4j + 1.5.0 + + + + com.microsoft.azure + azure-keyvault + 1.0.0 + +``` +***Please note*** as of the v6.2.2, the way to construct a `SQLServerColumnEncryptionAzureKeyVaultProvider` object has changed. Please refer to this [Wiki](https://github.com/Microsoft/mssql-jdbc/wiki/New-Constructor-Definition-for-SQLServerColumnEncryptionAzureKeyVaultProvider-after-6.2.2-Release) page for more information. + +## Guidelines for Creating Pull Requests +We love contributions from the community. To help improve the quality of our code, we encourage you to use the mssql-jdbc_formatter.xml formatter provided on all pull requests. + +Thank you! + +## Guidelines for Reporting Issues +We appreciate you taking the time to test the driver, provide feedback and report any issues. It would be extremely helpful if you: + +- Report each issue as a new issue (but check first if it's already been reported) +- Try to be detailed in your report. Useful information for good bug reports include: + * What you are seeing and what the expected behaviour is + * Which jar file? + * Environment details: e.g. Java version, client operating system? + * Table schema (for some issues the data types make a big difference!) + * Any other relevant information you want to share +- Try to include a Java sample demonstrating the isolated problem. + +Thank you! + +### Reporting security issues and security bugs +Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) [secure@microsoft.com](mailto:secure@microsoft.com). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the [Security TechCenter](https://technet.microsoft.com/en-us/security/ff852094.aspx). + +## Release roadmap and standards +Our goal is to release regular updates which improve the driver and bring new features to users. Stable, production quality releases happen twice a year, targeting the first and third quarters of the calendar year. They are tested against a comprehensive matrix of supported operating systems, Java versions, and SQL Server versions. Stable releases are accompanied by additional localized packages, which are available on the Microsoft website. + +Preview releases happen approximately monthly between stable releases. This gives users an opportunity to try out new features and provide feedback on them before they go into stable releases. Preview releases also include frequent bug fixes for customers to verify without having to wait for a stable release. Preview releases are only available in English. While they are tested, preview releases do not necessarily go through the same rigorous, full test matrix and review process as stable releases. + +You can see what is going into a future release by monitoring [Milestones](https://github.com/Microsoft/mssql-jdbc/milestones) in the repository. + +### Versioning convention +Starting with 6.0, stable versions have an even minor version. For example, 6.0, 6.2, 6.4. Preview versions have an odd minor version. For example, 6.1, 6.3, 6.5. + +## Contributors +Special thanks to everyone who has contributed to the project. + +Up-to-date list of contributors: https://github.com/Microsoft/mssql-jdbc/graphs/contributors + +- marschall (Philippe Marschall) +- pierresouchay (Pierre Souchay) +- gordthompson (Gord Thompson) +- gstojsic +- cosmofrit +- JamieMagee (Jamie Magee) +- mfriesen (Mike Friesen) +- tonytamwk +- sehrope (Sehrope Sarkuni) +- jacobovazquez +- brettwooldridge (Brett Wooldridge) + +## License +The Microsoft JDBC Driver for SQL Server is licensed under the MIT license. See the [LICENSE](https://github.com/Microsoft/mssql-jdbc/blob/master/LICENSE) file for more details. + +## Code of conduct +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + diff --git a/build.gradle b/build.gradle index bdd3c1090..bbb65a5fe 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ apply plugin: 'java' -version = '6.5.3-SNAPSHOT' +version = '6.5.3' def jreVersion = "" def testOutputDir = file("build/classes/java/test") def archivesBaseName = 'mssql-jdbc' diff --git a/pom.xml b/pom.xml index c9a7bb3ef..c35395c07 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.microsoft.sqlserver mssql-jdbc - 6.5.3-SNAPSHOT + 6.5.3 jar Microsoft JDBC Driver for SQL Server diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java index e8a537aa8..e26f27113 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java @@ -1,16 +1,16 @@ -/* - * 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; - -final class SQLJdbcVersion { - static final int major = 6; - static final int minor = 5; - static final int patch = 2; - static final int build = 0; -} +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + +package com.microsoft.sqlserver.jdbc; + +final class SQLJdbcVersion { + static final int major = 6; + static final int minor = 5; + static final int patch = 3; + static final int build = 0; +} From 6a3f676c073c13ca5ef5ebcabcf3307c67d0b79e Mon Sep 17 00:00:00 2001 From: rene-ye Date: Thu, 31 May 2018 17:01:31 -0700 Subject: [PATCH 45/84] Revert "changes for 6.5.3 preview release" This reverts commit 5c6ccd32d58a87d167e8fd661d0739c0c82e1965. --- CHANGELOG.md | 706 +++++++++--------- README.md | 418 +++++------ build.gradle | 2 +- pom.xml | 2 +- .../sqlserver/jdbc/SQLJdbcVersion.java | 32 +- 5 files changed, 572 insertions(+), 588 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 942a582ec..b5537523d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,361 +1,345 @@ -# Change Log -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/) - -## [6.5.3] Preview Release -### Added -- Added support for JDK 10 for both Maven and Gradle [#691](https://github.com/Microsoft/mssql-jdbc/pull/691) -- Added a resource bundle to handle junit error strings [#698](https://github.com/Microsoft/mssql-jdbc/pull/698) - -### Fixed Issues -- Fixed the driver disposing user created credentials when using Kerberos Constrained Delegation [#636](https://github.com/Microsoft/mssql-jdbc/pull/636) -- Fixed an issue with HostnameInCertificate when redirected while connected to Azure [#644](https://github.com/Microsoft/mssql-jdbc/pull/644) -- Fixed an intermittent issue with Prepared Statement handle not found [#648](https://github.com/Microsoft/mssql-jdbc/pull/648) -- Fixed a conflict with JDBC Compliance where the driver was returning marked columns as SS_IS_COMPUTED instead of IS_GENERATED [#695](https://github.com/Microsoft/mssql-jdbc/pull/695) -- Fixed maven build warnings and deprecated Java API warnings [#701](https://github.com/Microsoft/mssql-jdbc/pull/701) -- Fixed some Javadoc related warnings [#702](https://github.com/Microsoft/mssql-jdbc/pull/702) - -### Changed -- The old AKV constructor was re-added into the driver and marked as deprecated. This API may be removed in future versions of the driver. [#675](https://github.com/Microsoft/mssql-jdbc/pull/675) - -## [6.5.2] Preview Release -### Added -- Added new connection property "cancelQueryTimeout" to cancel QueryTimeout on Connection and Statement [#674](https://github.com/Microsoft/mssql-jdbc/pull/674) - -### Fixed Issues -- Improved performance degradation while maintaining JDBC compliance with results from sp_fkeys [#677](https://github.com/Microsoft/mssql-jdbc/pull/677) -- Fixed an issue where ResultSetMetaData instances created by a ResultSet that has been closed were not persisting [#685](https://github.com/Microsoft/mssql-jdbc/pull/685) -- Fixed an issue with PreparedStatement.setBigDecimal when no scale is passed [#684](https://github.com/Microsoft/mssql-jdbc/pull/684) -- Fixed an issue with Clobs/NClobs not persisting after ResultSet/Connection closes [#682](https://github.com/Microsoft/mssql-jdbc/pull/682) - -### Changed -- Updated the samples to be usable with Eclipse directly, and updated the driver version used by the samples to 6.4.0.jre9 [#679](https://github.com/Microsoft/mssql-jdbc/pull/679) -- Updated Gradle script for building JDBC Driver [#689](https://github.com/Microsoft/mssql-jdbc/pull/689) -- Updated Maven dependencies for test suite [#676](https://github.com/Microsoft/mssql-jdbc/pull/676) -- Updated multiple Maven dependency and plugin versions [#688](https://github.com/Microsoft/mssql-jdbc/pull/688) - -## [6.5.1] Preview Release -### Added -- Test cases for Date, Time, and Datetime2 data types. [#558](https://github.com/Microsoft/mssql-jdbc/pull/558) - -### Fixed Issues -- Fixed an issue where ResultSetMetadata returned incorrect columnType for Geometry and Geography data types [#657](https://github.com/Microsoft/mssql-jdbc/pull/657) -- Fixed server side CPU Affinity problems caused by uneven connection distribution across NUMA Nodes when multiSubnetFailover is true [#662](https://github.com/Microsoft/mssql-jdbc/pull/662) -- Fixed an issue where Driver wasn't parsing TDS Packets completely to capture exceptions raised inside executed stored procedures [#664](https://github.com/Microsoft/mssql-jdbc/pull/664) -- Fixed an issue where driver throws exception when using setMaxRows() followed by query execution when SHOWPLAN_TEXT is ON [#666](https://github.com/Microsoft/mssql-jdbc/pull/666) - -### Changed -- Removed unused imports which forced users to import the ADAL4J library [#652](https://github.com/Microsoft/mssql-jdbc/pull/652) - -## [6.5.0] Preview Release -### Added -- Support for spatial datatypes [#642](https://github.com/Microsoft/mssql-jdbc/pull/642) - -### Fixed Issues -- Fixed blobs becoming unavailable when the Result Set cursor moves or the Result Set closes [#595](https://github.com/Microsoft/mssql-jdbc/pull/595) -- Fixed an issue when attempting to insert an empty or null value into an encrypted column [#632](https://github.com/Microsoft/mssql-jdbc/pull/632) -- Fixed a misleading error message thrown by the driver when a user doesn't have execute permissions [#635](https://github.com/Microsoft/mssql-jdbc/pull/635) -- Fixed statements throwing SQLServerException instead of java.sql.SQLTimeoutException when the query times out [#641](https://github.com/Microsoft/mssql-jdbc/pull/641) - -### Changed -- Unit tests now use SQLException in most cases instead of SQLServerException. - -## [6.4.0] Stable Release -### Added -- Support added for AAD Integrated Authentication with ADAL4J on Windows/Linux/Mac OS [#603](https://github.com/Microsoft/mssql-jdbc/pull/603) -- Enable Recover after MSDTC is restarted [#581](https://github.com/Microsoft/mssql-jdbc/pull/581) -- Added Version Update configuration rules to project [#541](https://github.com/Microsoft/mssql-jdbc/pull/541) -- JDK 9 Compatibility + JDBC 4.3 API support added to the driver [#601 (https://github.com/Microsoft/mssql-jdbc/pull/601) - -### Fixed Issues -- Re-introduced Retry Logic for Prepared Statement Caching implementation and remove detect change context function [#618](https://github.com/Microsoft/mssql-jdbc/pull/618) and [#620](https://github.com/Microsoft/mssql-jdbc/pull/620) -- Fixes for SonarQube Reported issues [#599](https://github.com/Microsoft/mssql-jdbc/pull/599) -- Fixes for Random Assertion Errors [#597](https://github.com/Microsoft/mssql-jdbc/pull/597) - -### Changed -- Updated Appveyor to use JDK9 building driver and running tests [#619](https://github.com/Microsoft/mssql-jdbc/pull/619) -- JDK 7 compilation support removed from the driver [#601](https://github.com/Microsoft/mssql-jdbc/pull/601) - -## [6.3.6] Preview Release -### Added -- Added support for using database name as part of the key for handle cache [#561](https://github.com/Microsoft/mssql-jdbc/pull/561) -- Updated ADAL4J version to 1.3.0 and also added it into README file [#564](https://github.com/Microsoft/mssql-jdbc/pull/564) - -### Fixed Issues -- Fixed issues with static loggers being set by every constructor invocation [#563](https://github.com/Microsoft/mssql-jdbc/pull/563) - -## [6.3.5] Preview Release -### Added -- Added handle for Account Locked Exception 18486 during login in SQLServerConnection [#522](https://github.com/Microsoft/mssql-jdbc/pull/522) - -### Fixed Issues -- Fixed the issues with Prepared Statement Metadata Caching implementation [#543](https://github.com/Microsoft/mssql-jdbc/pull/543) -- Fixed issues with static logger member in abstract class 'SQLServerClobBase' [#537](https://github.com/Microsoft/mssql-jdbc/pull/537) - -## [6.3.4] Preview Release -### Added -- Added new ThreadGroup creation to prevent IllegalThreadStateException if the underlying ThreadGroup has been destroyed. [#474](https://github.com/Microsoft/mssql-jdbc/pull/474) -- Added try-with-resources to JUnit tests [#520](https://github.com/Microsoft/mssql-jdbc/pull/520) - -### Fixed Issues -- Fixed the issue with passing parameters names that start with '@' to a CallableStatement [#495](https://github.com/Microsoft/mssql-jdbc/pull/495) -- Fixed SQLServerDataTable creation being O(n^2) issue [#514](https://github.com/Microsoft/mssql-jdbc/pull/514) - -### Changed -- Changed some manual array copying to System.arraycopy() [#500](https://github.com/Microsoft/mssql-jdbc/pull/500) -- Removed redundant toString() on String objects [#501](https://github.com/Microsoft/mssql-jdbc/pull/501) -- Replaced literals with constants [#502](https://github.com/Microsoft/mssql-jdbc/pull/502) - -## [6.3.3] Preview Release -### Added -- Added connection properties for specifying custom TrustManager [#74](https://github.com/Microsoft/mssql-jdbc/pull/74) - -### Fixed Issues -- Fixed exception thrown by getters on null columns [#488](https://github.com/Microsoft/mssql-jdbc/pull/488) -- Fixed issue with DatabaseMetaData#getImportedKeys() returns wrong value for DELETE_RULE [#490](https://github.com/Microsoft/mssql-jdbc/pull/490) -- Fixed issue with ActivityCorrelator causing a classloader leak [#465](https://github.com/Microsoft/mssql-jdbc/pull/465) - -### Changed -- Removed explicit extends Object [#469](https://github.com/Microsoft/mssql-jdbc/pull/469) -- Removed unnecessary return statements [#471](https://github.com/Microsoft/mssql-jdbc/pull/471) -- Simplified overly complex boolean expressions [#472](https://github.com/Microsoft/mssql-jdbc/pull/472) -- Replaced explicit types with <> (the diamond operator) [#420](https://github.com/Microsoft/mssql-jdbc/pull/420) - -## [6.3.2] Preview Release -### Added -- Added new connection property: sslProtocol [#422](https://github.com/Microsoft/mssql-jdbc/pull/422) -- Added "slow" tag to long running tests [#461](https://github.com/Microsoft/mssql-jdbc/pull/461) - -### Fixed Issues -- Fixed some error messages [#452](https://github.com/Microsoft/mssql-jdbc/pull/452) & [#459](https://github.com/Microsoft/mssql-jdbc/pull/459) -- Fixed statement leaks [#455](https://github.com/Microsoft/mssql-jdbc/pull/455) -- Fixed an issue regarding to loginTimeout with TLS [#456](https://github.com/Microsoft/mssql-jdbc/pull/456) -- Fixed sql_variant issue with String type [#442](https://github.com/Microsoft/mssql-jdbc/pull/442) -- Fixed issue with throwing error message for unsupported datatype [#450](https://github.com/Microsoft/mssql-jdbc/pull/450) -- Fixed issue that initial batchException was not thrown [#458](https://github.com/Microsoft/mssql-jdbc/pull/458) - -### Changed -- Changed sendStringParameterAsUnicode to impact set/update null [#445](https://github.com/Microsoft/mssql-jdbc/pull/445) -- Removed connection property: fipsProvider [#460](https://github.com/Microsoft/mssql-jdbc/pull/460) -- Replaced for and while loops with foeach loops [#421](https://github.com/Microsoft/mssql-jdbc/pull/421) -- Replaced explicit types with the diamond operator [#468](https://github.com/Microsoft/mssql-jdbc/pull/468) & [#420](https://github.com/Microsoft/mssql-jdbc/pull/420) - -## [6.3.1] Preview Release -### Added -- Added support for datetime/smallDatetime in TVP [#435](https://github.com/Microsoft/mssql-jdbc/pull/435) -- Added more Junit tests for Always Encrypted [#432](https://github.com/Microsoft/mssql-jdbc/pull/432) - -### Fixed Issues -- Fixed getString issue for uniqueIdentifier [#423](https://github.com/Microsoft/mssql-jdbc/pull/423) - -### Changed -- Skip long running tests based on Tag [#425](https://github.com/Microsoft/mssql-jdbc/pull/425) -- Removed volatile keyword [#409](https://github.com/Microsoft/mssql-jdbc/pull/409) - -## [6.3.0] Preview Release -### Added -- Added support for sql_variant datatype [#387](https://github.com/Microsoft/mssql-jdbc/pull/387) -- Added more Junit tests for Always Encrypted [#404](https://github.com/Microsoft/mssql-jdbc/pull/404) - -### Fixed Issues -- Fixed Turkey locale issue when lowercasing an "i" [#384](https://github.com/Microsoft/mssql-jdbc/pull/384) -- Fixed issue with incorrect parameter count for INSERT with subquery [#373](https://github.com/Microsoft/mssql-jdbc/pull/373) -- Fixed issue with running DDL in PreparedStatement [#372](https://github.com/Microsoft/mssql-jdbc/pull/372) -- Fixed issue with parameter metadata with whitespace characters [#371](https://github.com/Microsoft/mssql-jdbc/pull/371) -- Fixed handling of explicit boxing and unboxing [#84](https://github.com/Microsoft/mssql-jdbc/pull/84) -- Fixed metadata caching batch query issue [#393](https://github.com/Microsoft/mssql-jdbc/pull/393) -- Fixed javadoc issue for the newest maven version [#385](https://github.com/Microsoft/mssql-jdbc/pull/385) - -### Changed -- Updated ADAL4J dependency to version 1.2.0 [#392](https://github.com/Microsoft/mssql-jdbc/pull/392) -- Updated azure-keyvault dependency to version 1.0.0 [#397](https://github.com/Microsoft/mssql-jdbc/pull/397) - -## [6.2.2] Hotfix & Stable Release -### Changed -- Updated ADAL4J to version 1.2.0 and AKV to version 1.0.0 [#516](https://github.com/Microsoft/mssql-jdbc/pull/516) - -## [6.2.1] Hotfix & Stable Release -### Fixed Issues -- Fixed queries without parameters using preparedStatement [#372](https://github.com/Microsoft/mssql-jdbc/pull/372) -### Changed -- Removed metadata caching [#377](https://github.com/Microsoft/mssql-jdbc/pull/377) - -## [6.2.0] Release Candidate -### Added -- Added TVP and BulkCopy random data test for all data types with server cursor [#319](https://github.com/Microsoft/mssql-jdbc/pull/319) -- Added AE setup and test [#337](https://github.com/Microsoft/mssql-jdbc/pull/337),[328](https://github.com/Microsoft/mssql-jdbc/pull/328) -- Added validation for javadocs for every commit [#338](https://github.com/Microsoft/mssql-jdbc/pull/338) -- Added metdata caching [#345](https://github.com/Microsoft/mssql-jdbc/pull/345) -- Added caching mvn dependencies for Appveyor [#320](https://github.com/Microsoft/mssql-jdbc/pull/320) -- Added caching mvn dependencies for Travis-CI [#322](https://github.com/Microsoft/mssql-jdbc/pull/322) -- Added handle for bulkcopy exceptions [#286](https://github.com/Microsoft/mssql-jdbc/pull/286) -- Added handle for TVP exceptions [#285](https://github.com/Microsoft/mssql-jdbc/pull/285) - -### Fixed Issues -- Fixed metadata caching issue with AE on connection [#361](https://github.com/Microsoft/mssql-jdbc/pull/361) -- Fixed issue with String index out of range parameter metadata [#353](https://github.com/Microsoft/mssql-jdbc/pull/353) -- Fixed javaDocs [#354](https://github.com/Microsoft/mssql-jdbc/pull/354) -- Fixed javaDocs [#299](https://github.com/Microsoft/mssql-jdbc/pull/299) -- Performance fix from @brettwooldridge [#347](https://github.com/Microsoft/mssql-jdbc/pull/347) -- Get local host name before opening TDSChannel [#324](https://github.com/Microsoft/mssql-jdbc/pull/324) -- Fixed TVP Time issue [#317](https://github.com/Microsoft/mssql-jdbc/pull/317) -- Fixed SonarQube issues [#300](https://github.com/Microsoft/mssql-jdbc/pull/300) -- Fixed SonarQube issues [#301](https://github.com/Microsoft/mssql-jdbc/pull/301) -- Fixed random TDS invalid error [#310](https://github.com/Microsoft/mssql-jdbc/pull/310) -- Fixed password logging [#298](https://github.com/Microsoft/mssql-jdbc/pull/298) -- Fixed bulkcopy cursor issue [#270](https://github.com/Microsoft/mssql-jdbc/pull/270) - -### Changed -- Refresh Kerberos configuration [#279](https://github.com/Microsoft/mssql-jdbc/pull/279) - -## [6.1.7] Preview Release -### Added -- Added support for data type LONGVARCHAR, LONGNVARCHAR, LONGVARBINARY and SQLXML in TVP [#259](https://github.com/Microsoft/mssql-jdbc/pull/259) -- Added new connection property to accept custom JAAS configuration for Kerberos [#254](https://github.com/Microsoft/mssql-jdbc/pull/254) -- Added support for server cursor with TVP [#234](https://github.com/Microsoft/mssql-jdbc/pull/234) -- Experimental Feature: Added new connection property to support network timeout [#253](https://github.com/Microsoft/mssql-jdbc/pull/253) -- Added support to authenticate Kerberos with principal and password [#163](https://github.com/Microsoft/mssql-jdbc/pull/163) -- Added temporal types to BulkCopyCSVTestInput.csv [#262](https://github.com/Microsoft/mssql-jdbc/pull/262) -- Added automatic detection of REALM in SPN needed for Cross Domain authentication [#40](https://github.com/Microsoft/mssql-jdbc/pull/40) - -### Changed -- Updated minor semantics [#232](https://github.com/Microsoft/mssql-jdbc/pull/232) -- Cleaned up Azure Active Directory (AAD) Authentication methods [#256](https://github.com/Microsoft/mssql-jdbc/pull/256) -- Updated permission check before setting network timeout [#255](https://github.com/Microsoft/mssql-jdbc/pull/255) - -### Fixed Issues -- Turn TNIR (TransparentNetworkIPResolution) off for Azure Active Directory (AAD) Authentication and changed TNIR multipliers [#240](https://github.com/Microsoft/mssql-jdbc/pull/240) -- Wrapped ClassCastException in BulkCopy with SQLServerException [#260](https://github.com/Microsoft/mssql-jdbc/pull/260) -- Initialized the XA transaction manager for each XAResource [#257](https://github.com/Microsoft/mssql-jdbc/pull/257) -- Fixed BigDecimal scale rounding issue in BulkCopy [#230](https://github.com/Microsoft/mssql-jdbc/issues/230) -- Fixed the invalid exception thrown when stored procedure does not exist is used with TVP [#265](https://github.com/Microsoft/mssql-jdbc/pull/265) - -## [6.1.6] Preview Release -### Added -- Added constrained delegation to connection sample [#188](https://github.com/Microsoft/mssql-jdbc/pull/188) -- Added snapshot to identify nightly/dev builds [#221](https://github.com/Microsoft/mssql-jdbc/pull/221) -- Clarifying public deprecated constructors in LOBs [#226](https://github.com/Microsoft/mssql-jdbc/pull/226) -- Added OSGI Headers in MANIFEST.MF [#218](https://github.com/Microsoft/mssql-jdbc/pull/218) -- Added cause to SQLServerException [#202](https://github.com/Microsoft/mssql-jdbc/pull/202) - -### Changed -- Removed java.io.Serializable interface from SQLServerConnectionPoolProxy [#201](https://github.com/Microsoft/mssql-jdbc/pull/201) -- Refactored DROP TABLE and DROP PROCEDURE calls in test code [#222](https://github.com/Microsoft/mssql-jdbc/pull/222/files) -- Removed obsolete methods from DriverJDBCVersion [#187](https://github.com/Microsoft/mssql-jdbc/pull/187) - -### Fixed Issues -- Typos in SQLServerConnectionPoolProxy [#189](https://github.com/Microsoft/mssql-jdbc/pull/189) -- Fixed issue where exceptions are thrown if comments are in a SQL string [#157](https://github.com/Microsoft/mssql-jdbc/issues/157) -- Fixed test failures on pre-2016 servers [#215](https://github.com/Microsoft/mssql-jdbc/pull/215) -- Fixed SQLServerExceptions that are wrapped by another SQLServerException [#213](https://github.com/Microsoft/mssql-jdbc/pull/213) -- Fixed a stream isClosed error on LOBs test [#233](https://github.com/Microsoft/mssql-jdbc/pull/223) -- LOBs are fully materialised [#16](https://github.com/Microsoft/mssql-jdbc/issues/16) -- Fix precision issue in TVP [#217](https://github.com/Microsoft/mssql-jdbc/pull/217) -- Re-interrupt the current thread in order to restore the threads interrupt status [#196](https://github.com/Microsoft/mssql-jdbc/issues/196) -- Re-use parameter metadata when using Always Encrypted [#195](https://github.com/Microsoft/mssql-jdbc/issues/195) -- Improved performance for PreparedStatements through minimized server round-trips [#166](https://github.com/Microsoft/mssql-jdbc/issues/166) - -## [6.1.5] Preview Release -### Added -- Added socket timeout exception as cause[#180](https://github.com/Microsoft/mssql-jdbc/pull/180) -- Added Constrained delegation support[#178](https://github.com/Microsoft/mssql-jdbc/pull/178) -- Added junit test for Statement test[#174](https://github.com/Microsoft/mssql-jdbc/pull/174) -- Added test for statement.cancel() when MultiSubnetFailover is set to true[#173](https://github.com/Microsoft/mssql-jdbc/pull/173) -- Added tests for lobs [#168](https://github.com/Microsoft/mssql-jdbc/pull/168) -- Added badges for License, Maven Central, JavaDocs & gitter chat room [#184](https://github.com/Microsoft/mssql-jdbc/pull/184) - -### Changed -- Enabled update counts for SELECT INTO statements[#175](https://github.com/Microsoft/mssql-jdbc/pull/175) -- Use Executor service instead of thread[#162](https://github.com/Microsoft/mssql-jdbc/pull/162) -- Convert socket adaptor to socket[#160](https://github.com/Microsoft/mssql-jdbc/pull/160) - -### Fixed Issues -- Fixed local test failures [#179](https://github.com/Microsoft/mssql-jdbc/pull/179) -- Fixed random failure in BulkCopyColumnMapping test[#165](https://github.com/Microsoft/mssql-jdbc/pull/165) - -## [6.1.4] Preview Release -### Added -- Added isWrapperFor methods for MetaData classes[#94](https://github.com/Microsoft/mssql-jdbc/pull/94) -- Added Code Coverage [#136](https://github.com/Microsoft/mssql-jdbc/pull/136) -- Added TVP schema test [#137](https://github.com/Microsoft/mssql-jdbc/pull/137) -- Introduced FIPS boolean property [#135](https://github.com/Microsoft/mssql-jdbc/pull/135) -- Added unit statement test cases [#147](https://github.com/Microsoft/mssql-jdbc/pull/147) - -### Changed -- Enabled AAD Authentication with Access Token on Linux [#142](https://github.com/Microsoft/mssql-jdbc/pull/142) -- Enabled AAD Authentication with ActiveDirectoryPassword on Linux [#146](https://github.com/Microsoft/mssql-jdbc/pull/146) -- Made Azure Key Vault and Azure Active Directory Authentication Dependencies optional [#148](https://github.com/Microsoft/mssql-jdbc/pull/148) -- Getting TVP name from ParameterMetaData when using TVP with a stored procedure [#138](https://github.com/Microsoft/mssql-jdbc/pull/138) - -### Fixed Issues -- Fixed getBinaryStream issue [#133](https://github.com/Microsoft/mssql-jdbc/pull/133) -- Fixed an issue of Bulk Copy when AlwaysEncrypted is enabled on connection and destination table is not encrypted [#151](https://github.com/Microsoft/mssql-jdbc/pull/151) - - -## [6.1.3] Preview Release -### Added - - Added Binary and Varbinary types to the jUnit test framework [#119](https://github.com/Microsoft/mssql-jdbc/pull/119) - - Added BulkCopy test cases for csv [#123](https://github.com/Microsoft/mssql-jdbc/pull/123) - - Added BulkCopy ColumnMapping test cases [#127](https://github.com/Microsoft/mssql-jdbc/pull/127) - -### Changed - - Switched to clean rounding for bigDecimal [#118](https://github.com/Microsoft/mssql-jdbc/pull/118) - - Updated BVT tests to use jUnit test framework [#120](https://github.com/Microsoft/mssql-jdbc/pull/120) - - In case of socket timeout occurance, avoid connection retry [#122](https://github.com/Microsoft/mssql-jdbc/pull/122) - - Changed ant build file to skip tests [#126](https://github.com/Microsoft/mssql-jdbc/pull/126) - -### Fixed Issues - - Fixed the inconsistent coding style [#4](https://github.com/Microsoft/mssql-jdbc/issues/4) - - Fixed NullPointerException in case when SocketTimeout occurs [#65](https://github.com/Microsoft/mssql-jdbc/issues/121) - - -## [6.1.2] Preview Release -### Added - - Socket timeout implementation for both connection string and data source [#85](https://github.com/Microsoft/mssql-jdbc/pull/85) - - Query timeout API for datasource [#88](https://github.com/Microsoft/mssql-jdbc/pull/88) - - Added connection tests [#95](https://github.com/Microsoft/mssql-jdbc/pull/95) - - Added Support for FIPS enabled JVM [#97](https://github.com/Microsoft/mssql-jdbc/pull/97) - - Added additional tests for bulk copy [#110] (https://github.com/Microsoft/mssql-jdbc/pull/110) - -### Changed - - Remove redundant type casts [#63](https://github.com/Microsoft/mssql-jdbc/pull/63) - - Read SQL Server error message if status flag has DONE_ERROR set [#73](https://github.com/Microsoft/mssql-jdbc/pull/73) - - Fix a bug when the value of queryTimeout is bigger than the max value of integer [#78](https://github.com/Microsoft/mssql-jdbc/pull/78) - - Add new dependencies to gradle build script [#81](https://github.com/Microsoft/mssql-jdbc/pull/81) - - Updates to test framework [#90](https://github.com/Microsoft/mssql-jdbc/pull/90) - -### Fixed Issues - - Set the jre8 version as default [#59](https://github.com/Microsoft/mssql-jdbc/issues/59) - - Fixed exception SQL Server instance in use does not support column encryption [#65](https://github.com/Microsoft/mssql-jdbc/issues/65) - - TVP Handling is causing exception when calling SP with return value [#80](https://github.com/Microsoft/mssql-jdbc/issues/80) - - BigDecimal in TVP can incorrectly cause SQLServerException related to invalid precision or scale [#86](https://github.com/Microsoft/mssql-jdbc/issues/86) - - Fixed the connection close issue on using variant type [#91] (https://github.com/Microsoft/mssql-jdbc/issues/91) - - -## [6.1.1] Preview Release -### Added -- Java Docs [#46](https://github.com/Microsoft/mssql-jdbc/pull/46) -- Driver version number in LOGIN7 packet [#43](https://github.com/Microsoft/mssql-jdbc/pull/43) -- Travis- CI Integration [#23](https://github.com/Microsoft/mssql-jdbc/pull/23) -- Appveyor Integration [#23](https://github.com/Microsoft/mssql-jdbc/pull/23) -- Make Ms Jdbc driver more Spring friendly [#9](https://github.com/Microsoft/mssql-jdbc/pull/9) -- Implement Driver#getParentLogger [#8](https://github.com/Microsoft/mssql-jdbc/pull/8) -- Implement missing MetaData #unwrap methods [#12](https://github.com/Microsoft/mssql-jdbc/pull/12) -- Added Gradle build script [#54](https://github.com/Microsoft/mssql-jdbc/pull/54) -- Added a queryTimeout connection parameter [#45](https://github.com/Microsoft/mssql-jdbc/pull/45) -- Added Stored Procedure support for TVP [#47](https://github.com/Microsoft/mssql-jdbc/pull/47) - -### Changed -- Use StandardCharsets [#15](https://github.com/Microsoft/mssql-jdbc/pull/15) -- Use Charset throughout [#26](https://github.com/Microsoft/mssql-jdbc/pull/26) -- Upgrade azure-keyvault to 0.9.7 [#50](https://github.com/Microsoft/mssql-jdbc/pull/50) -- Avoid unnecessary calls to String copy constructor [#14](https://github.com/Microsoft/mssql-jdbc/pull/14) -- make setObject() throw a clear exception for TVP when using with result set [#48](https://github.com/Microsoft/mssql-jdbc/pull/48) -- Few clean-ups like remove wild card imports, unused imports etc. [#52](https://github.com/Microsoft/mssql-jdbc/pull/52) -- Update Maven Plugin [#55](https://github.com/Microsoft/mssql-jdbc/pull/55) - - -## [6.1.0] Stable Release -### Changed -- Open Sourced. +# Change Log +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) + +## [6.5.2] Preview Release +### Added +- Added new connection property "cancelQueryTimeout" to cancel QueryTimeout on Connection and Statement [#674](https://github.com/Microsoft/mssql-jdbc/pull/674) + +### Fixed Issues +- Improved performance degradation while maintaining JDBC compliance with results from sp_fkeys [#677](https://github.com/Microsoft/mssql-jdbc/pull/677) +- Fixed an issue where ResultSetMetaData instances created by a ResultSet that has been closed were not persisting [#685](https://github.com/Microsoft/mssql-jdbc/pull/685) +- Fixed an issue with PreparedStatement.setBigDecimal when no scale is passed [#684](https://github.com/Microsoft/mssql-jdbc/pull/684) +- Fixed an issue with Clobs/NClobs not persisting after ResultSet/Connection closes [#682](https://github.com/Microsoft/mssql-jdbc/pull/682) + +### Changed +- Updated the samples to be usable with Eclipse directly, and updated the driver version used by the samples to 6.4.0.jre9 [#679](https://github.com/Microsoft/mssql-jdbc/pull/679) +- Updated Gradle script for building JDBC Driver [#689](https://github.com/Microsoft/mssql-jdbc/pull/689) +- Updated Maven dependencies for test suite [#676](https://github.com/Microsoft/mssql-jdbc/pull/676) +- Updated multiple Maven dependency and plugin versions [#688](https://github.com/Microsoft/mssql-jdbc/pull/688) + +## [6.5.1] Preview Release +### Added +- Test cases for Date, Time, and Datetime2 data types. [#558](https://github.com/Microsoft/mssql-jdbc/pull/558) + +### Fixed Issues +- Fixed an issue where ResultSetMetadata returned incorrect columnType for Geometry and Geography data types [#657](https://github.com/Microsoft/mssql-jdbc/pull/657) +- Fixed server side CPU Affinity problems caused by uneven connection distribution across NUMA Nodes when multiSubnetFailover is true [#662](https://github.com/Microsoft/mssql-jdbc/pull/662) +- Fixed an issue where Driver wasn't parsing TDS Packets completely to capture exceptions raised inside executed stored procedures [#664](https://github.com/Microsoft/mssql-jdbc/pull/664) +- Fixed an issue where driver throws exception when using setMaxRows() followed by query execution when SHOWPLAN_TEXT is ON [#666](https://github.com/Microsoft/mssql-jdbc/pull/666) + +### Changed +- Removed unused imports which forced users to import the ADAL4J library [#652](https://github.com/Microsoft/mssql-jdbc/pull/652) + +## [6.5.0] Preview Release +### Added +- Support for spatial datatypes [#642](https://github.com/Microsoft/mssql-jdbc/pull/642) + +### Fixed Issues +- Fixed blobs becoming unavailable when the Result Set cursor moves or the Result Set closes [#595](https://github.com/Microsoft/mssql-jdbc/pull/595) +- Fixed an issue when attempting to insert an empty or null value into an encrypted column [#632](https://github.com/Microsoft/mssql-jdbc/pull/632) +- Fixed a misleading error message thrown by the driver when a user doesn't have execute permissions [#635](https://github.com/Microsoft/mssql-jdbc/pull/635) +- Fixed statements throwing SQLServerException instead of java.sql.SQLTimeoutException when the query times out [#641](https://github.com/Microsoft/mssql-jdbc/pull/641) + +### Changed +- Unit tests now use SQLException in most cases instead of SQLServerException. + +## [6.4.0] Stable Release +### Added +- Support added for AAD Integrated Authentication with ADAL4J on Windows/Linux/Mac OS [#603](https://github.com/Microsoft/mssql-jdbc/pull/603) +- Enable Recover after MSDTC is restarted [#581](https://github.com/Microsoft/mssql-jdbc/pull/581) +- Added Version Update configuration rules to project [#541](https://github.com/Microsoft/mssql-jdbc/pull/541) +- JDK 9 Compatibility + JDBC 4.3 API support added to the driver [#601 (https://github.com/Microsoft/mssql-jdbc/pull/601) + +### Fixed Issues +- Re-introduced Retry Logic for Prepared Statement Caching implementation and remove detect change context function [#618](https://github.com/Microsoft/mssql-jdbc/pull/618) and [#620](https://github.com/Microsoft/mssql-jdbc/pull/620) +- Fixes for SonarQube Reported issues [#599](https://github.com/Microsoft/mssql-jdbc/pull/599) +- Fixes for Random Assertion Errors [#597](https://github.com/Microsoft/mssql-jdbc/pull/597) + +### Changed +- Updated Appveyor to use JDK9 building driver and running tests [#619](https://github.com/Microsoft/mssql-jdbc/pull/619) +- JDK 7 compilation support removed from the driver [#601](https://github.com/Microsoft/mssql-jdbc/pull/601) + +## [6.3.6] Preview Release +### Added +- Added support for using database name as part of the key for handle cache [#561](https://github.com/Microsoft/mssql-jdbc/pull/561) +- Updated ADAL4J version to 1.3.0 and also added it into README file [#564](https://github.com/Microsoft/mssql-jdbc/pull/564) + +### Fixed Issues +- Fixed issues with static loggers being set by every constructor invocation [#563](https://github.com/Microsoft/mssql-jdbc/pull/563) + +## [6.3.5] Preview Release +### Added +- Added handle for Account Locked Exception 18486 during login in SQLServerConnection [#522](https://github.com/Microsoft/mssql-jdbc/pull/522) + +### Fixed Issues +- Fixed the issues with Prepared Statement Metadata Caching implementation [#543](https://github.com/Microsoft/mssql-jdbc/pull/543) +- Fixed issues with static logger member in abstract class 'SQLServerClobBase' [#537](https://github.com/Microsoft/mssql-jdbc/pull/537) + +## [6.3.4] Preview Release +### Added +- Added new ThreadGroup creation to prevent IllegalThreadStateException if the underlying ThreadGroup has been destroyed. [#474](https://github.com/Microsoft/mssql-jdbc/pull/474) +- Added try-with-resources to JUnit tests [#520](https://github.com/Microsoft/mssql-jdbc/pull/520) + +### Fixed Issues +- Fixed the issue with passing parameters names that start with '@' to a CallableStatement [#495](https://github.com/Microsoft/mssql-jdbc/pull/495) +- Fixed SQLServerDataTable creation being O(n^2) issue [#514](https://github.com/Microsoft/mssql-jdbc/pull/514) + +### Changed +- Changed some manual array copying to System.arraycopy() [#500](https://github.com/Microsoft/mssql-jdbc/pull/500) +- Removed redundant toString() on String objects [#501](https://github.com/Microsoft/mssql-jdbc/pull/501) +- Replaced literals with constants [#502](https://github.com/Microsoft/mssql-jdbc/pull/502) + +## [6.3.3] Preview Release +### Added +- Added connection properties for specifying custom TrustManager [#74](https://github.com/Microsoft/mssql-jdbc/pull/74) + +### Fixed Issues +- Fixed exception thrown by getters on null columns [#488](https://github.com/Microsoft/mssql-jdbc/pull/488) +- Fixed issue with DatabaseMetaData#getImportedKeys() returns wrong value for DELETE_RULE [#490](https://github.com/Microsoft/mssql-jdbc/pull/490) +- Fixed issue with ActivityCorrelator causing a classloader leak [#465](https://github.com/Microsoft/mssql-jdbc/pull/465) + +### Changed +- Removed explicit extends Object [#469](https://github.com/Microsoft/mssql-jdbc/pull/469) +- Removed unnecessary return statements [#471](https://github.com/Microsoft/mssql-jdbc/pull/471) +- Simplified overly complex boolean expressions [#472](https://github.com/Microsoft/mssql-jdbc/pull/472) +- Replaced explicit types with <> (the diamond operator) [#420](https://github.com/Microsoft/mssql-jdbc/pull/420) + +## [6.3.2] Preview Release +### Added +- Added new connection property: sslProtocol [#422](https://github.com/Microsoft/mssql-jdbc/pull/422) +- Added "slow" tag to long running tests [#461](https://github.com/Microsoft/mssql-jdbc/pull/461) + +### Fixed Issues +- Fixed some error messages [#452](https://github.com/Microsoft/mssql-jdbc/pull/452) & [#459](https://github.com/Microsoft/mssql-jdbc/pull/459) +- Fixed statement leaks [#455](https://github.com/Microsoft/mssql-jdbc/pull/455) +- Fixed an issue regarding to loginTimeout with TLS [#456](https://github.com/Microsoft/mssql-jdbc/pull/456) +- Fixed sql_variant issue with String type [#442](https://github.com/Microsoft/mssql-jdbc/pull/442) +- Fixed issue with throwing error message for unsupported datatype [#450](https://github.com/Microsoft/mssql-jdbc/pull/450) +- Fixed issue that initial batchException was not thrown [#458](https://github.com/Microsoft/mssql-jdbc/pull/458) + +### Changed +- Changed sendStringParameterAsUnicode to impact set/update null [#445](https://github.com/Microsoft/mssql-jdbc/pull/445) +- Removed connection property: fipsProvider [#460](https://github.com/Microsoft/mssql-jdbc/pull/460) +- Replaced for and while loops with foeach loops [#421](https://github.com/Microsoft/mssql-jdbc/pull/421) +- Replaced explicit types with the diamond operator [#468](https://github.com/Microsoft/mssql-jdbc/pull/468) & [#420](https://github.com/Microsoft/mssql-jdbc/pull/420) + +## [6.3.1] Preview Release +### Added +- Added support for datetime/smallDatetime in TVP [#435](https://github.com/Microsoft/mssql-jdbc/pull/435) +- Added more Junit tests for Always Encrypted [#432](https://github.com/Microsoft/mssql-jdbc/pull/432) + +### Fixed Issues +- Fixed getString issue for uniqueIdentifier [#423](https://github.com/Microsoft/mssql-jdbc/pull/423) + +### Changed +- Skip long running tests based on Tag [#425](https://github.com/Microsoft/mssql-jdbc/pull/425) +- Removed volatile keyword [#409](https://github.com/Microsoft/mssql-jdbc/pull/409) + +## [6.3.0] Preview Release +### Added +- Added support for sql_variant datatype [#387](https://github.com/Microsoft/mssql-jdbc/pull/387) +- Added more Junit tests for Always Encrypted [#404](https://github.com/Microsoft/mssql-jdbc/pull/404) + +### Fixed Issues +- Fixed Turkey locale issue when lowercasing an "i" [#384](https://github.com/Microsoft/mssql-jdbc/pull/384) +- Fixed issue with incorrect parameter count for INSERT with subquery [#373](https://github.com/Microsoft/mssql-jdbc/pull/373) +- Fixed issue with running DDL in PreparedStatement [#372](https://github.com/Microsoft/mssql-jdbc/pull/372) +- Fixed issue with parameter metadata with whitespace characters [#371](https://github.com/Microsoft/mssql-jdbc/pull/371) +- Fixed handling of explicit boxing and unboxing [#84](https://github.com/Microsoft/mssql-jdbc/pull/84) +- Fixed metadata caching batch query issue [#393](https://github.com/Microsoft/mssql-jdbc/pull/393) +- Fixed javadoc issue for the newest maven version [#385](https://github.com/Microsoft/mssql-jdbc/pull/385) + +### Changed +- Updated ADAL4J dependency to version 1.2.0 [#392](https://github.com/Microsoft/mssql-jdbc/pull/392) +- Updated azure-keyvault dependency to version 1.0.0 [#397](https://github.com/Microsoft/mssql-jdbc/pull/397) + +## [6.2.2] Hotfix & Stable Release +### Changed +- Updated ADAL4J to version 1.2.0 and AKV to version 1.0.0 [#516](https://github.com/Microsoft/mssql-jdbc/pull/516) + +## [6.2.1] Hotfix & Stable Release +### Fixed Issues +- Fixed queries without parameters using preparedStatement [#372](https://github.com/Microsoft/mssql-jdbc/pull/372) +### Changed +- Removed metadata caching [#377](https://github.com/Microsoft/mssql-jdbc/pull/377) + +## [6.2.0] Release Candidate +### Added +- Added TVP and BulkCopy random data test for all data types with server cursor [#319](https://github.com/Microsoft/mssql-jdbc/pull/319) +- Added AE setup and test [#337](https://github.com/Microsoft/mssql-jdbc/pull/337),[328](https://github.com/Microsoft/mssql-jdbc/pull/328) +- Added validation for javadocs for every commit [#338](https://github.com/Microsoft/mssql-jdbc/pull/338) +- Added metdata caching [#345](https://github.com/Microsoft/mssql-jdbc/pull/345) +- Added caching mvn dependencies for Appveyor [#320](https://github.com/Microsoft/mssql-jdbc/pull/320) +- Added caching mvn dependencies for Travis-CI [#322](https://github.com/Microsoft/mssql-jdbc/pull/322) +- Added handle for bulkcopy exceptions [#286](https://github.com/Microsoft/mssql-jdbc/pull/286) +- Added handle for TVP exceptions [#285](https://github.com/Microsoft/mssql-jdbc/pull/285) + +### Fixed Issues +- Fixed metadata caching issue with AE on connection [#361](https://github.com/Microsoft/mssql-jdbc/pull/361) +- Fixed issue with String index out of range parameter metadata [#353](https://github.com/Microsoft/mssql-jdbc/pull/353) +- Fixed javaDocs [#354](https://github.com/Microsoft/mssql-jdbc/pull/354) +- Fixed javaDocs [#299](https://github.com/Microsoft/mssql-jdbc/pull/299) +- Performance fix from @brettwooldridge [#347](https://github.com/Microsoft/mssql-jdbc/pull/347) +- Get local host name before opening TDSChannel [#324](https://github.com/Microsoft/mssql-jdbc/pull/324) +- Fixed TVP Time issue [#317](https://github.com/Microsoft/mssql-jdbc/pull/317) +- Fixed SonarQube issues [#300](https://github.com/Microsoft/mssql-jdbc/pull/300) +- Fixed SonarQube issues [#301](https://github.com/Microsoft/mssql-jdbc/pull/301) +- Fixed random TDS invalid error [#310](https://github.com/Microsoft/mssql-jdbc/pull/310) +- Fixed password logging [#298](https://github.com/Microsoft/mssql-jdbc/pull/298) +- Fixed bulkcopy cursor issue [#270](https://github.com/Microsoft/mssql-jdbc/pull/270) + +### Changed +- Refresh Kerberos configuration [#279](https://github.com/Microsoft/mssql-jdbc/pull/279) + +## [6.1.7] Preview Release +### Added +- Added support for data type LONGVARCHAR, LONGNVARCHAR, LONGVARBINARY and SQLXML in TVP [#259](https://github.com/Microsoft/mssql-jdbc/pull/259) +- Added new connection property to accept custom JAAS configuration for Kerberos [#254](https://github.com/Microsoft/mssql-jdbc/pull/254) +- Added support for server cursor with TVP [#234](https://github.com/Microsoft/mssql-jdbc/pull/234) +- Experimental Feature: Added new connection property to support network timeout [#253](https://github.com/Microsoft/mssql-jdbc/pull/253) +- Added support to authenticate Kerberos with principal and password [#163](https://github.com/Microsoft/mssql-jdbc/pull/163) +- Added temporal types to BulkCopyCSVTestInput.csv [#262](https://github.com/Microsoft/mssql-jdbc/pull/262) +- Added automatic detection of REALM in SPN needed for Cross Domain authentication [#40](https://github.com/Microsoft/mssql-jdbc/pull/40) + +### Changed +- Updated minor semantics [#232](https://github.com/Microsoft/mssql-jdbc/pull/232) +- Cleaned up Azure Active Directory (AAD) Authentication methods [#256](https://github.com/Microsoft/mssql-jdbc/pull/256) +- Updated permission check before setting network timeout [#255](https://github.com/Microsoft/mssql-jdbc/pull/255) + +### Fixed Issues +- Turn TNIR (TransparentNetworkIPResolution) off for Azure Active Directory (AAD) Authentication and changed TNIR multipliers [#240](https://github.com/Microsoft/mssql-jdbc/pull/240) +- Wrapped ClassCastException in BulkCopy with SQLServerException [#260](https://github.com/Microsoft/mssql-jdbc/pull/260) +- Initialized the XA transaction manager for each XAResource [#257](https://github.com/Microsoft/mssql-jdbc/pull/257) +- Fixed BigDecimal scale rounding issue in BulkCopy [#230](https://github.com/Microsoft/mssql-jdbc/issues/230) +- Fixed the invalid exception thrown when stored procedure does not exist is used with TVP [#265](https://github.com/Microsoft/mssql-jdbc/pull/265) + +## [6.1.6] Preview Release +### Added +- Added constrained delegation to connection sample [#188](https://github.com/Microsoft/mssql-jdbc/pull/188) +- Added snapshot to identify nightly/dev builds [#221](https://github.com/Microsoft/mssql-jdbc/pull/221) +- Clarifying public deprecated constructors in LOBs [#226](https://github.com/Microsoft/mssql-jdbc/pull/226) +- Added OSGI Headers in MANIFEST.MF [#218](https://github.com/Microsoft/mssql-jdbc/pull/218) +- Added cause to SQLServerException [#202](https://github.com/Microsoft/mssql-jdbc/pull/202) + +### Changed +- Removed java.io.Serializable interface from SQLServerConnectionPoolProxy [#201](https://github.com/Microsoft/mssql-jdbc/pull/201) +- Refactored DROP TABLE and DROP PROCEDURE calls in test code [#222](https://github.com/Microsoft/mssql-jdbc/pull/222/files) +- Removed obsolete methods from DriverJDBCVersion [#187](https://github.com/Microsoft/mssql-jdbc/pull/187) + +### Fixed Issues +- Typos in SQLServerConnectionPoolProxy [#189](https://github.com/Microsoft/mssql-jdbc/pull/189) +- Fixed issue where exceptions are thrown if comments are in a SQL string [#157](https://github.com/Microsoft/mssql-jdbc/issues/157) +- Fixed test failures on pre-2016 servers [#215](https://github.com/Microsoft/mssql-jdbc/pull/215) +- Fixed SQLServerExceptions that are wrapped by another SQLServerException [#213](https://github.com/Microsoft/mssql-jdbc/pull/213) +- Fixed a stream isClosed error on LOBs test [#233](https://github.com/Microsoft/mssql-jdbc/pull/223) +- LOBs are fully materialised [#16](https://github.com/Microsoft/mssql-jdbc/issues/16) +- Fix precision issue in TVP [#217](https://github.com/Microsoft/mssql-jdbc/pull/217) +- Re-interrupt the current thread in order to restore the threads interrupt status [#196](https://github.com/Microsoft/mssql-jdbc/issues/196) +- Re-use parameter metadata when using Always Encrypted [#195](https://github.com/Microsoft/mssql-jdbc/issues/195) +- Improved performance for PreparedStatements through minimized server round-trips [#166](https://github.com/Microsoft/mssql-jdbc/issues/166) + +## [6.1.5] Preview Release +### Added +- Added socket timeout exception as cause[#180](https://github.com/Microsoft/mssql-jdbc/pull/180) +- Added Constrained delegation support[#178](https://github.com/Microsoft/mssql-jdbc/pull/178) +- Added junit test for Statement test[#174](https://github.com/Microsoft/mssql-jdbc/pull/174) +- Added test for statement.cancel() when MultiSubnetFailover is set to true[#173](https://github.com/Microsoft/mssql-jdbc/pull/173) +- Added tests for lobs [#168](https://github.com/Microsoft/mssql-jdbc/pull/168) +- Added badges for License, Maven Central, JavaDocs & gitter chat room [#184](https://github.com/Microsoft/mssql-jdbc/pull/184) + +### Changed +- Enabled update counts for SELECT INTO statements[#175](https://github.com/Microsoft/mssql-jdbc/pull/175) +- Use Executor service instead of thread[#162](https://github.com/Microsoft/mssql-jdbc/pull/162) +- Convert socket adaptor to socket[#160](https://github.com/Microsoft/mssql-jdbc/pull/160) + +### Fixed Issues +- Fixed local test failures [#179](https://github.com/Microsoft/mssql-jdbc/pull/179) +- Fixed random failure in BulkCopyColumnMapping test[#165](https://github.com/Microsoft/mssql-jdbc/pull/165) + +## [6.1.4] Preview Release +### Added +- Added isWrapperFor methods for MetaData classes[#94](https://github.com/Microsoft/mssql-jdbc/pull/94) +- Added Code Coverage [#136](https://github.com/Microsoft/mssql-jdbc/pull/136) +- Added TVP schema test [#137](https://github.com/Microsoft/mssql-jdbc/pull/137) +- Introduced FIPS boolean property [#135](https://github.com/Microsoft/mssql-jdbc/pull/135) +- Added unit statement test cases [#147](https://github.com/Microsoft/mssql-jdbc/pull/147) + +### Changed +- Enabled AAD Authentication with Access Token on Linux [#142](https://github.com/Microsoft/mssql-jdbc/pull/142) +- Enabled AAD Authentication with ActiveDirectoryPassword on Linux [#146](https://github.com/Microsoft/mssql-jdbc/pull/146) +- Made Azure Key Vault and Azure Active Directory Authentication Dependencies optional [#148](https://github.com/Microsoft/mssql-jdbc/pull/148) +- Getting TVP name from ParameterMetaData when using TVP with a stored procedure [#138](https://github.com/Microsoft/mssql-jdbc/pull/138) + +### Fixed Issues +- Fixed getBinaryStream issue [#133](https://github.com/Microsoft/mssql-jdbc/pull/133) +- Fixed an issue of Bulk Copy when AlwaysEncrypted is enabled on connection and destination table is not encrypted [#151](https://github.com/Microsoft/mssql-jdbc/pull/151) + + +## [6.1.3] Preview Release +### Added + - Added Binary and Varbinary types to the jUnit test framework [#119](https://github.com/Microsoft/mssql-jdbc/pull/119) + - Added BulkCopy test cases for csv [#123](https://github.com/Microsoft/mssql-jdbc/pull/123) + - Added BulkCopy ColumnMapping test cases [#127](https://github.com/Microsoft/mssql-jdbc/pull/127) + +### Changed + - Switched to clean rounding for bigDecimal [#118](https://github.com/Microsoft/mssql-jdbc/pull/118) + - Updated BVT tests to use jUnit test framework [#120](https://github.com/Microsoft/mssql-jdbc/pull/120) + - In case of socket timeout occurance, avoid connection retry [#122](https://github.com/Microsoft/mssql-jdbc/pull/122) + - Changed ant build file to skip tests [#126](https://github.com/Microsoft/mssql-jdbc/pull/126) + +### Fixed Issues + - Fixed the inconsistent coding style [#4](https://github.com/Microsoft/mssql-jdbc/issues/4) + - Fixed NullPointerException in case when SocketTimeout occurs [#65](https://github.com/Microsoft/mssql-jdbc/issues/121) + + +## [6.1.2] Preview Release +### Added + - Socket timeout implementation for both connection string and data source [#85](https://github.com/Microsoft/mssql-jdbc/pull/85) + - Query timeout API for datasource [#88](https://github.com/Microsoft/mssql-jdbc/pull/88) + - Added connection tests [#95](https://github.com/Microsoft/mssql-jdbc/pull/95) + - Added Support for FIPS enabled JVM [#97](https://github.com/Microsoft/mssql-jdbc/pull/97) + - Added additional tests for bulk copy [#110] (https://github.com/Microsoft/mssql-jdbc/pull/110) + +### Changed + - Remove redundant type casts [#63](https://github.com/Microsoft/mssql-jdbc/pull/63) + - Read SQL Server error message if status flag has DONE_ERROR set [#73](https://github.com/Microsoft/mssql-jdbc/pull/73) + - Fix a bug when the value of queryTimeout is bigger than the max value of integer [#78](https://github.com/Microsoft/mssql-jdbc/pull/78) + - Add new dependencies to gradle build script [#81](https://github.com/Microsoft/mssql-jdbc/pull/81) + - Updates to test framework [#90](https://github.com/Microsoft/mssql-jdbc/pull/90) + +### Fixed Issues + - Set the jre8 version as default [#59](https://github.com/Microsoft/mssql-jdbc/issues/59) + - Fixed exception SQL Server instance in use does not support column encryption [#65](https://github.com/Microsoft/mssql-jdbc/issues/65) + - TVP Handling is causing exception when calling SP with return value [#80](https://github.com/Microsoft/mssql-jdbc/issues/80) + - BigDecimal in TVP can incorrectly cause SQLServerException related to invalid precision or scale [#86](https://github.com/Microsoft/mssql-jdbc/issues/86) + - Fixed the connection close issue on using variant type [#91] (https://github.com/Microsoft/mssql-jdbc/issues/91) + + +## [6.1.1] Preview Release +### Added +- Java Docs [#46](https://github.com/Microsoft/mssql-jdbc/pull/46) +- Driver version number in LOGIN7 packet [#43](https://github.com/Microsoft/mssql-jdbc/pull/43) +- Travis- CI Integration [#23](https://github.com/Microsoft/mssql-jdbc/pull/23) +- Appveyor Integration [#23](https://github.com/Microsoft/mssql-jdbc/pull/23) +- Make Ms Jdbc driver more Spring friendly [#9](https://github.com/Microsoft/mssql-jdbc/pull/9) +- Implement Driver#getParentLogger [#8](https://github.com/Microsoft/mssql-jdbc/pull/8) +- Implement missing MetaData #unwrap methods [#12](https://github.com/Microsoft/mssql-jdbc/pull/12) +- Added Gradle build script [#54](https://github.com/Microsoft/mssql-jdbc/pull/54) +- Added a queryTimeout connection parameter [#45](https://github.com/Microsoft/mssql-jdbc/pull/45) +- Added Stored Procedure support for TVP [#47](https://github.com/Microsoft/mssql-jdbc/pull/47) + +### Changed +- Use StandardCharsets [#15](https://github.com/Microsoft/mssql-jdbc/pull/15) +- Use Charset throughout [#26](https://github.com/Microsoft/mssql-jdbc/pull/26) +- Upgrade azure-keyvault to 0.9.7 [#50](https://github.com/Microsoft/mssql-jdbc/pull/50) +- Avoid unnecessary calls to String copy constructor [#14](https://github.com/Microsoft/mssql-jdbc/pull/14) +- make setObject() throw a clear exception for TVP when using with result set [#48](https://github.com/Microsoft/mssql-jdbc/pull/48) +- Few clean-ups like remove wild card imports, unused imports etc. [#52](https://github.com/Microsoft/mssql-jdbc/pull/52) +- Update Maven Plugin [#55](https://github.com/Microsoft/mssql-jdbc/pull/55) + + +## [6.1.0] Stable Release +### Changed +- Open Sourced. diff --git a/README.md b/README.md index 346abe951..b80bdb198 100644 --- a/README.md +++ b/README.md @@ -1,209 +1,209 @@ -[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/Microsoft/mssql-jdbc/master/LICENSE) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.microsoft.sqlserver/mssql-jdbc/badge.svg)](http://mvnrepository.com/artifact/com.microsoft.sqlserver/mssql-jdbc) -[![codecov.io](http://codecov.io/github/Microsoft/mssql-jdbc/coverage.svg?branch=master)](http://codecov.io/github/Microsoft/mssql-jdbc?branch=master) -[![Javadocs](http://javadoc.io/badge/com.microsoft.sqlserver/mssql-jdbc.svg)](http://javadoc.io/doc/com.microsoft.sqlserver/mssql-jdbc) -[![Gitter](https://img.shields.io/gitter/room/badges/shields.svg)](https://gitter.im/Microsoft/mssql-developers) -
-# Microsoft JDBC Driver for SQL Server - -Welcome to the Microsoft JDBC Driver for SQL Server project! - -The Microsoft JDBC Driver for SQL Server is a Type 4 JDBC driver that provides database connectivity through the standard JDBC application program interfaces (APIs) available in the Java Platform, Enterprise Editions. The Driver provides access to Microsoft SQL Server and Azure SQL Database from any Java application, application server, or Java-enabled applet. - -We hope you enjoy using the Microsoft JDBC Driver for SQL Server. - -SQL Server Team - -## Take our survey - -Let us know how you think we're doing. - - - -## Status of Most Recent Builds -| AppVeyor (Windows) | Travis CI (Linux) | -|--------------------------|--------------------------| -| [![AppVeyor ](https://ci.appveyor.com/api/projects/status/o6fjg16678ol64d3?svg=true "Windows")](https://ci.appveyor.com/project/Microsoft-JDBC/mssql-jdbc) | [![Travis CI](https://travis-ci.org/Microsoft/mssql-jdbc.svg? "Linux")](https://travis-ci.org/Microsoft/mssql-jdbc ) |vg? "Linux" - -## Announcements -What's coming next? We will look into adding a more comprehensive set of tests, improving our javadocs, and start developing the next set of features. - -## Get Started -* [**Ubuntu + SQL Server + Java**](https://www.microsoft.com/en-us/sql-server/developer-get-started/java/ubuntu) -* [**Red Hat + SQL Server + Java**](https://www.microsoft.com/en-us/sql-server/developer-get-started/java/rhel) -* [**Mac + SQL Server + Java**](https://www.microsoft.com/en-us/sql-server/developer-get-started/java/mac) -* [**Windows + SQL Server + Java**](https://www.microsoft.com/en-us/sql-server/developer-get-started/java/windows) - -## Build -### Prerequisites -* Java 9 -* [Maven](http://maven.apache.org/download.cgi) -* An instance of SQL Server or Azure SQL Database that you can connect to. - -### Build the JAR files -Maven builds automatically trigger a set of verification tests to run. For these tests to pass, you will first need to add an environment variable in your system called `mssql_jdbc_test_connection_properties` to provide the [correct connection properties](https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url) for your SQL Server or Azure SQL Database instance. - -To build the jar files, you must use Java 9 with Maven. You can choose to build a JDBC 4.3 compliant jar file (for use with JRE 9) and/or a JDBC 4.2 compliant jar file (for use with JRE 8). - -* Maven: - 1. If you have not already done so, add the environment variable `mssql_jdbc_test_connection_properties` in your system with the connection properties for your SQL Server or SQL DB instance. - 2. Run one of the commands below to build a JDBC 4.3 compliant jar or JDBC 4.2 compliant jar in the \target directory. - * Run `mvn install -Pbuild43`. This creates JDBC 4.3 compliant jar in \target directory - * Run `mvn install -Pbuild42`. This creates JDBC 4.2 compliant jar in \target directory - -* Gradle: - 1. If you have not already done so, add the environment variable `mssql_jdbc_test_connection_properties` in your system with the connection properties for your SQL Server or SQL DB instance. - 2. Run one of the commands below to build a JDBC 4.3 compliant jar or JDBC 4.2 compliant jar in the \build\libs directory. - * Run `gradle build -PbuildProfile=build43`. This creates JDBC 4.3 compliant jar in \build\libs directory - * Run `gradle build -PbuildProfile=build42`. This creates JDBC 4.2 compliant jar in \build\libs directory - -## Resources - -### Documentation -API reference documentation is available in [Javadocs](https://aka.ms/jdbcjavadocs). - -This driver is documented on [Microsoft's Documentation web site](https://docs.microsoft.com/en-us/sql/connect/jdbc/getting-started-with-the-jdbc-driver). - -### Sample Code -For samples, please see the src\sample directory. - -### Download the DLLs -For some features (e.g. Integrated Authentication and Distributed Transactions), you may need to use the `sqljdbc_xa` and `sqljdbc_auth` DLLs. They can be downloaded from the [Microsoft Download Center](https://go.microsoft.com/fwlink/?linkid=868287) - -### Download the driver -Don't want to compile anything? - -We're now on the Maven Central Repository. Add the following to your POM file to get the most stable release: -```xml - - com.microsoft.sqlserver - mssql-jdbc - 6.4.0.jre9 - -``` -The driver can be downloaded from the [Microsoft Download Center](https://go.microsoft.com/fwlink/?linkid=868287). - -To get the latest preview version of the driver, add the following to your POM file: -```xml - - com.microsoft.sqlserver - mssql-jdbc - 6.5.3.jre9-preview - -``` - - - -## Dependencies -This project has following dependencies: - -Compile Time: - - `azure-keyvault` : Azure Key Vault Provider for Always Encrypted Azure Key Vault feature (optional) - - `adal4j` : Azure ActiveDirectory Library for Java for Azure Active Directory Authentication feature and Azure Key Vault feature (optional) - -Test Time: - - `junit:jar` : For Unit Test cases. - -### Dependency Tree -One can see all dependencies including Transitive Dependency by executing following command. -``` -mvn dependency:tree -``` - -### Azure Key Vault and Azure Active Directory Authentication Dependencies -Projects that require either of the two features need to explicitly declare the dependency in their pom file. - -***For Example:*** If you are using *Azure Active Directory Authentication feature* then you need to redeclare *adal4j* dependency in your project's pom file. Please see the following snippet: -```xml - - com.microsoft.sqlserver - mssql-jdbc - 6.5.3.jre9-preview - compile - - - - com.microsoft.azure - adal4j - 1.5.0 - -``` - -***For Example:*** If you are using *Azure Key Vault feature* then you need to redeclare *azure-keyvault* dependency and *adal4j* dependency in your project's pom file. Please see the following snippet: -```xml - - com.microsoft.sqlserver - mssql-jdbc - 6.5.3.jre9-preview - compile - - - - com.microsoft.azure - adal4j - 1.5.0 - - - - com.microsoft.azure - azure-keyvault - 1.0.0 - -``` -***Please note*** as of the v6.2.2, the way to construct a `SQLServerColumnEncryptionAzureKeyVaultProvider` object has changed. Please refer to this [Wiki](https://github.com/Microsoft/mssql-jdbc/wiki/New-Constructor-Definition-for-SQLServerColumnEncryptionAzureKeyVaultProvider-after-6.2.2-Release) page for more information. - -## Guidelines for Creating Pull Requests -We love contributions from the community. To help improve the quality of our code, we encourage you to use the mssql-jdbc_formatter.xml formatter provided on all pull requests. - -Thank you! - -## Guidelines for Reporting Issues -We appreciate you taking the time to test the driver, provide feedback and report any issues. It would be extremely helpful if you: - -- Report each issue as a new issue (but check first if it's already been reported) -- Try to be detailed in your report. Useful information for good bug reports include: - * What you are seeing and what the expected behaviour is - * Which jar file? - * Environment details: e.g. Java version, client operating system? - * Table schema (for some issues the data types make a big difference!) - * Any other relevant information you want to share -- Try to include a Java sample demonstrating the isolated problem. - -Thank you! - -### Reporting security issues and security bugs -Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) [secure@microsoft.com](mailto:secure@microsoft.com). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the [Security TechCenter](https://technet.microsoft.com/en-us/security/ff852094.aspx). - -## Release roadmap and standards -Our goal is to release regular updates which improve the driver and bring new features to users. Stable, production quality releases happen twice a year, targeting the first and third quarters of the calendar year. They are tested against a comprehensive matrix of supported operating systems, Java versions, and SQL Server versions. Stable releases are accompanied by additional localized packages, which are available on the Microsoft website. - -Preview releases happen approximately monthly between stable releases. This gives users an opportunity to try out new features and provide feedback on them before they go into stable releases. Preview releases also include frequent bug fixes for customers to verify without having to wait for a stable release. Preview releases are only available in English. While they are tested, preview releases do not necessarily go through the same rigorous, full test matrix and review process as stable releases. - -You can see what is going into a future release by monitoring [Milestones](https://github.com/Microsoft/mssql-jdbc/milestones) in the repository. - -### Versioning convention -Starting with 6.0, stable versions have an even minor version. For example, 6.0, 6.2, 6.4. Preview versions have an odd minor version. For example, 6.1, 6.3, 6.5. - -## Contributors -Special thanks to everyone who has contributed to the project. - -Up-to-date list of contributors: https://github.com/Microsoft/mssql-jdbc/graphs/contributors - -- marschall (Philippe Marschall) -- pierresouchay (Pierre Souchay) -- gordthompson (Gord Thompson) -- gstojsic -- cosmofrit -- JamieMagee (Jamie Magee) -- mfriesen (Mike Friesen) -- tonytamwk -- sehrope (Sehrope Sarkuni) -- jacobovazquez -- brettwooldridge (Brett Wooldridge) - -## License -The Microsoft JDBC Driver for SQL Server is licensed under the MIT license. See the [LICENSE](https://github.com/Microsoft/mssql-jdbc/blob/master/LICENSE) file for more details. - -## Code of conduct -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. - +[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/Microsoft/mssql-jdbc/master/LICENSE) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.microsoft.sqlserver/mssql-jdbc/badge.svg)](http://mvnrepository.com/artifact/com.microsoft.sqlserver/mssql-jdbc) +[![codecov.io](http://codecov.io/github/Microsoft/mssql-jdbc/coverage.svg?branch=master)](http://codecov.io/github/Microsoft/mssql-jdbc?branch=master) +[![Javadocs](http://javadoc.io/badge/com.microsoft.sqlserver/mssql-jdbc.svg)](http://javadoc.io/doc/com.microsoft.sqlserver/mssql-jdbc) +[![Gitter](https://img.shields.io/gitter/room/badges/shields.svg)](https://gitter.im/Microsoft/mssql-developers) +
+# Microsoft JDBC Driver for SQL Server + +Welcome to the Microsoft JDBC Driver for SQL Server project! + +The Microsoft JDBC Driver for SQL Server is a Type 4 JDBC driver that provides database connectivity through the standard JDBC application program interfaces (APIs) available in the Java Platform, Enterprise Editions. The Driver provides access to Microsoft SQL Server and Azure SQL Database from any Java application, application server, or Java-enabled applet. + +We hope you enjoy using the Microsoft JDBC Driver for SQL Server. + +SQL Server Team + +## Take our survey + +Let us know how you think we're doing. + + + +## Status of Most Recent Builds +| AppVeyor (Windows) | Travis CI (Linux) | +|--------------------------|--------------------------| +| [![AppVeyor ](https://ci.appveyor.com/api/projects/status/o6fjg16678ol64d3?svg=true "Windows")](https://ci.appveyor.com/project/Microsoft-JDBC/mssql-jdbc) | [![Travis CI](https://travis-ci.org/Microsoft/mssql-jdbc.svg? "Linux")](https://travis-ci.org/Microsoft/mssql-jdbc ) |vg? "Linux" + +## Announcements +What's coming next? We will look into adding a more comprehensive set of tests, improving our javadocs, and start developing the next set of features. + +## Get Started +* [**Ubuntu + SQL Server + Java**](https://www.microsoft.com/en-us/sql-server/developer-get-started/java/ubuntu) +* [**Red Hat + SQL Server + Java**](https://www.microsoft.com/en-us/sql-server/developer-get-started/java/rhel) +* [**Mac + SQL Server + Java**](https://www.microsoft.com/en-us/sql-server/developer-get-started/java/mac) +* [**Windows + SQL Server + Java**](https://www.microsoft.com/en-us/sql-server/developer-get-started/java/windows) + +## Build +### Prerequisites +* Java 9 +* [Maven](http://maven.apache.org/download.cgi) +* An instance of SQL Server or Azure SQL Database that you can connect to. + +### Build the JAR files +Maven builds automatically trigger a set of verification tests to run. For these tests to pass, you will first need to add an environment variable in your system called `mssql_jdbc_test_connection_properties` to provide the [correct connection properties](https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url) for your SQL Server or Azure SQL Database instance. + +To build the jar files, you must use Java 9 with Maven. You can choose to build a JDBC 4.3 compliant jar file (for use with JRE 9) and/or a JDBC 4.2 compliant jar file (for use with JRE 8). + +* Maven: + 1. If you have not already done so, add the environment variable `mssql_jdbc_test_connection_properties` in your system with the connection properties for your SQL Server or SQL DB instance. + 2. Run one of the commands below to build a JDBC 4.3 compliant jar or JDBC 4.2 compliant jar in the \target directory. + * Run `mvn install -Pbuild43`. This creates JDBC 4.3 compliant jar in \target directory + * Run `mvn install -Pbuild42`. This creates JDBC 4.2 compliant jar in \target directory + +* Gradle: + 1. If you have not already done so, add the environment variable `mssql_jdbc_test_connection_properties` in your system with the connection properties for your SQL Server or SQL DB instance. + 2. Run one of the commands below to build a JDBC 4.3 compliant jar or JDBC 4.2 compliant jar in the \build\libs directory. + * Run `gradle build -PbuildProfile=build43`. This creates JDBC 4.3 compliant jar in \build\libs directory + * Run `gradle build -PbuildProfile=build42`. This creates JDBC 4.2 compliant jar in \build\libs directory + +## Resources + +### Documentation +API reference documentation is available in [Javadocs](https://aka.ms/jdbcjavadocs). + +This driver is documented on [Microsoft's Documentation web site](https://docs.microsoft.com/en-us/sql/connect/jdbc/getting-started-with-the-jdbc-driver). + +### Sample Code +For samples, please see the src\sample directory. + +### Download the DLLs +For some features (e.g. Integrated Authentication and Distributed Transactions), you may need to use the `sqljdbc_xa` and `sqljdbc_auth` DLLs. They can be downloaded from the [Microsoft Download Center](https://go.microsoft.com/fwlink/?linkid=868287) + +### Download the driver +Don't want to compile anything? + +We're now on the Maven Central Repository. Add the following to your POM file to get the most stable release: +```xml + + com.microsoft.sqlserver + mssql-jdbc + 6.4.0.jre9 + +``` +The driver can be downloaded from the [Microsoft Download Center](https://go.microsoft.com/fwlink/?linkid=868287). + +To get the latest preview version of the driver, add the following to your POM file: +```xml + + com.microsoft.sqlserver + mssql-jdbc + 6.5.2.jre9-preview + +``` + + + +## Dependencies +This project has following dependencies: + +Compile Time: + - `azure-keyvault` : Azure Key Vault Provider for Always Encrypted Azure Key Vault feature (optional) + - `adal4j` : Azure ActiveDirectory Library for Java for Azure Active Directory Authentication feature and Azure Key Vault feature (optional) + +Test Time: + - `junit:jar` : For Unit Test cases. + +### Dependency Tree +One can see all dependencies including Transitive Dependency by executing following command. +``` +mvn dependency:tree +``` + +### Azure Key Vault and Azure Active Directory Authentication Dependencies +Projects that require either of the two features need to explicitly declare the dependency in their pom file. + +***For Example:*** If you are using *Azure Active Directory Authentication feature* then you need to redeclare *adal4j* dependency in your project's pom file. Please see the following snippet: +```xml + + com.microsoft.sqlserver + mssql-jdbc + 6.5.2.jre9-preview + compile + + + + com.microsoft.azure + adal4j + 1.5.0 + +``` + +***For Example:*** If you are using *Azure Key Vault feature* then you need to redeclare *azure-keyvault* dependency and *adal4j* dependency in your project's pom file. Please see the following snippet: +```xml + + com.microsoft.sqlserver + mssql-jdbc + 6.5.2.jre9-preview + compile + + + + com.microsoft.azure + adal4j + 1.5.0 + + + + com.microsoft.azure + azure-keyvault + 1.0.0 + +``` +***Please note*** as of the v6.2.2, the way to construct a `SQLServerColumnEncryptionAzureKeyVaultProvider` object has changed. Please refer to this [Wiki](https://github.com/Microsoft/mssql-jdbc/wiki/New-Constructor-Definition-for-SQLServerColumnEncryptionAzureKeyVaultProvider-after-6.2.2-Release) page for more information. + +## Guidelines for Creating Pull Requests +We love contributions from the community. To help improve the quality of our code, we encourage you to use the mssql-jdbc_formatter.xml formatter provided on all pull requests. + +Thank you! + +## Guidelines for Reporting Issues +We appreciate you taking the time to test the driver, provide feedback and report any issues. It would be extremely helpful if you: + +- Report each issue as a new issue (but check first if it's already been reported) +- Try to be detailed in your report. Useful information for good bug reports include: + * What you are seeing and what the expected behaviour is + * Which jar file? + * Environment details: e.g. Java version, client operating system? + * Table schema (for some issues the data types make a big difference!) + * Any other relevant information you want to share +- Try to include a Java sample demonstrating the isolated problem. + +Thank you! + +### Reporting security issues and security bugs +Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) [secure@microsoft.com](mailto:secure@microsoft.com). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the [Security TechCenter](https://technet.microsoft.com/en-us/security/ff852094.aspx). + +## Release roadmap and standards +Our goal is to release regular updates which improve the driver and bring new features to users. Stable, production quality releases happen twice a year, targeting the first and third quarters of the calendar year. They are tested against a comprehensive matrix of supported operating systems, Java versions, and SQL Server versions. Stable releases are accompanied by additional localized packages, which are available on the Microsoft website. + +Preview releases happen approximately monthly between stable releases. This gives users an opportunity to try out new features and provide feedback on them before they go into stable releases. Preview releases also include frequent bug fixes for customers to verify without having to wait for a stable release. Preview releases are only available in English. While they are tested, preview releases do not necessarily go through the same rigorous, full test matrix and review process as stable releases. + +You can see what is going into a future release by monitoring [Milestones](https://github.com/Microsoft/mssql-jdbc/milestones) in the repository. + +### Versioning convention +Starting with 6.0, stable versions have an even minor version. For example, 6.0, 6.2, 6.4. Preview versions have an odd minor version. For example, 6.1, 6.3, 6.5. + +## Contributors +Special thanks to everyone who has contributed to the project. + +Up-to-date list of contributors: https://github.com/Microsoft/mssql-jdbc/graphs/contributors + +- marschall (Philippe Marschall) +- pierresouchay (Pierre Souchay) +- gordthompson (Gord Thompson) +- gstojsic +- cosmofrit +- JamieMagee (Jamie Magee) +- mfriesen (Mike Friesen) +- tonytamwk +- sehrope (Sehrope Sarkuni) +- jacobovazquez +- brettwooldridge (Brett Wooldridge) + +## License +The Microsoft JDBC Driver for SQL Server is licensed under the MIT license. See the [LICENSE](https://github.com/Microsoft/mssql-jdbc/blob/master/LICENSE) file for more details. + +## Code of conduct +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + diff --git a/build.gradle b/build.gradle index bbb65a5fe..bdd3c1090 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ apply plugin: 'java' -version = '6.5.3' +version = '6.5.3-SNAPSHOT' def jreVersion = "" def testOutputDir = file("build/classes/java/test") def archivesBaseName = 'mssql-jdbc' diff --git a/pom.xml b/pom.xml index c35395c07..c9a7bb3ef 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.microsoft.sqlserver mssql-jdbc - 6.5.3 + 6.5.3-SNAPSHOT jar Microsoft JDBC Driver for SQL Server diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java index e26f27113..e8a537aa8 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java @@ -1,16 +1,16 @@ -/* - * 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; - -final class SQLJdbcVersion { - static final int major = 6; - static final int minor = 5; - static final int patch = 3; - static final int build = 0; -} +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + +package com.microsoft.sqlserver.jdbc; + +final class SQLJdbcVersion { + static final int major = 6; + static final int minor = 5; + static final int patch = 2; + static final int build = 0; +} From c8e638df4dc2c414b0aa8a7a11a2df9a267beb71 Mon Sep 17 00:00:00 2001 From: v-reye Date: Fri, 1 Jun 2018 11:11:48 -0700 Subject: [PATCH 46/84] Changes in preparation for 6.5.3 preview release (#710) * changes for preview release * requested changes * jre version update changes --- CHANGELOG.md | 14 ++++++++++++++ README.md | 10 +++++----- build.gradle | 2 +- pom.xml | 2 +- .../microsoft/sqlserver/jdbc/SQLJdbcVersion.java | 2 +- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5537523d..c5181e0c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) +## [6.5.3] Preview Release +### Added +- Added removed constructor back to AKV Provider which supports authentication with a customized method to fetch accessToken [#675](https://github.com/Microsoft/mssql-jdbc/pull/675) +- Added support for JDK 10 for both Maven and Gradle [#691](https://github.com/Microsoft/mssql-jdbc/pull/691) +- Added a resource bundle to handle junit error strings [#698](https://github.com/Microsoft/mssql-jdbc/pull/698) + +### Fixed Issues +- Fixed the driver disposing user created credentials when using Kerberos Constrained Delegation [#636](https://github.com/Microsoft/mssql-jdbc/pull/636) +- Fixed an issue with HostnameInCertificate when redirected while connected to Azure [#644](https://github.com/Microsoft/mssql-jdbc/pull/644) +- Fixed an intermittent issue with Prepared Statement handle not found [#648](https://github.com/Microsoft/mssql-jdbc/pull/648) +- Fixed a conflict with JDBC Compliance where the driver was returning marked columns as SS_IS_COMPUTED instead of IS_GENERATED [#695](https://github.com/Microsoft/mssql-jdbc/pull/695) +- Fixed maven build warnings and deprecated Java API warnings [#701](https://github.com/Microsoft/mssql-jdbc/pull/701) +- Fixed some Javadoc related warnings [#702](https://github.com/Microsoft/mssql-jdbc/pull/702) + ## [6.5.2] Preview Release ### Added - Added new connection property "cancelQueryTimeout" to cancel QueryTimeout on Connection and Statement [#674](https://github.com/Microsoft/mssql-jdbc/pull/674) diff --git a/README.md b/README.md index b80bdb198..23ea036a4 100644 --- a/README.md +++ b/README.md @@ -36,14 +36,14 @@ What's coming next? We will look into adding a more comprehensive set of tests, ## Build ### Prerequisites -* Java 9 +* Java 10 * [Maven](http://maven.apache.org/download.cgi) * An instance of SQL Server or Azure SQL Database that you can connect to. ### Build the JAR files Maven builds automatically trigger a set of verification tests to run. For these tests to pass, you will first need to add an environment variable in your system called `mssql_jdbc_test_connection_properties` to provide the [correct connection properties](https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url) for your SQL Server or Azure SQL Database instance. -To build the jar files, you must use Java 9 with Maven. You can choose to build a JDBC 4.3 compliant jar file (for use with JRE 9) and/or a JDBC 4.2 compliant jar file (for use with JRE 8). +To build the jar files, you must use Java 10 with Maven. You can choose to build a JDBC 4.3 compliant jar file (for use with JRE 10) and/or a JDBC 4.2 compliant jar file (for use with JRE 8). * Maven: 1. If you have not already done so, add the environment variable `mssql_jdbc_test_connection_properties` in your system with the connection properties for your SQL Server or SQL DB instance. @@ -88,7 +88,7 @@ To get the latest preview version of the driver, add the following to your POM f com.microsoft.sqlserver mssql-jdbc - 6.5.2.jre9-preview + 6.5.3.jre10-preview ``` @@ -118,7 +118,7 @@ Projects that require either of the two features need to explicitly declare the com.microsoft.sqlserver mssql-jdbc - 6.5.2.jre9-preview + 6.5.3.jre10-preview compile @@ -134,7 +134,7 @@ Projects that require either of the two features need to explicitly declare the com.microsoft.sqlserver mssql-jdbc - 6.5.2.jre9-preview + 6.5.3.jre10-preview compile diff --git a/build.gradle b/build.gradle index bdd3c1090..bbb65a5fe 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ apply plugin: 'java' -version = '6.5.3-SNAPSHOT' +version = '6.5.3' def jreVersion = "" def testOutputDir = file("build/classes/java/test") def archivesBaseName = 'mssql-jdbc' diff --git a/pom.xml b/pom.xml index c9a7bb3ef..c35395c07 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.microsoft.sqlserver mssql-jdbc - 6.5.3-SNAPSHOT + 6.5.3 jar Microsoft JDBC Driver for SQL Server diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java index e8a537aa8..964bb7ec9 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java @@ -11,6 +11,6 @@ final class SQLJdbcVersion { static final int major = 6; static final int minor = 5; - static final int patch = 2; + static final int patch = 3; static final int build = 0; } From c3f0a21ed4fcab82252c0d734d945361bd98aea8 Mon Sep 17 00:00:00 2001 From: rene-ye Date: Fri, 1 Jun 2018 15:59:44 -0700 Subject: [PATCH 47/84] snapshot updates post release --- build.gradle | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index bbb65a5fe..a3fd3a8c7 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ apply plugin: 'java' -version = '6.5.3' +version = '6.5.4-SNAPSHOT' def jreVersion = "" def testOutputDir = file("build/classes/java/test") def archivesBaseName = 'mssql-jdbc' diff --git a/pom.xml b/pom.xml index c35395c07..7d6be1480 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.microsoft.sqlserver mssql-jdbc - 6.5.3 + 6.5.4-SNAPSHOT jar Microsoft JDBC Driver for SQL Server From c20435499f00a875ff0c7d79f900139b4e6f14e8 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Mon, 4 Jun 2018 17:34:10 -0700 Subject: [PATCH 48/84] remove on_dw, and remove redundant fmtonly --- .../sqlserver/jdbc/SQLServerBulkCopy.java | 20 +++++++-- .../sqlserver/jdbc/SQLServerConnection.java | 10 ++--- .../sqlserver/jdbc/SQLServerDataSource.java | 14 +++---- .../sqlserver/jdbc/SQLServerDriver.java | 4 +- .../jdbc/SQLServerPreparedStatement.java | 41 ++++++++++--------- .../sqlserver/jdbc/SQLServerResource.java | 2 +- 6 files changed, 52 insertions(+), 39 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java index 732ae9c0f..ba36cbda4 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java @@ -156,6 +156,8 @@ private class ColumnMapping { /* Statement level encryption setting needed for querying against encrypted columns. */ private SQLServerStatementColumnEncryptionSetting stmtColumnEncriptionSetting = SQLServerStatementColumnEncryptionSetting.UseConnectionSetting; + private ResultSet destinationTableMetadata; + /* * Metadata for the destination table columns */ @@ -1746,10 +1748,16 @@ private void getDestinationMetadata() throws SQLServerException { SQLServerStatement stmt = null; try { - stmt = (SQLServerStatement) connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, - ResultSet.CONCUR_READ_ONLY, connection.getHoldability(), stmtColumnEncriptionSetting); - // Get destination metadata - rs = stmt.executeQueryInternal("SET FMTONLY ON SELECT * FROM " + destinationTableName + " SET FMTONLY OFF "); + if (null == destinationTableMetadata) { + stmt = (SQLServerStatement) connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, + connection.getHoldability(), stmtColumnEncriptionSetting); + + // Get destination metadata + rs = stmt.executeQueryInternal("sp_executesql N'SET FMTONLY ON SELECT * FROM " + destinationTableName + " '"); + } + else { + rs = (SQLServerResultSet) destinationTableMetadata; + } destColumnCount = rs.getMetaData().getColumnCount(); destColumnMetadata = new HashMap<>(); @@ -3584,4 +3592,8 @@ private boolean writeBatchData(TDSWriter tdsWriter, protected void setStmtColumnEncriptionSetting(SQLServerStatementColumnEncryptionSetting stmtColumnEncriptionSetting) { this.stmtColumnEncriptionSetting = stmtColumnEncriptionSetting; } + + protected void setDestinationTableMetadata(SQLServerResultSet rs) { + destinationTableMetadata = rs; + } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 523a3c185..d094d2055 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -496,10 +496,10 @@ final int getSocketTimeoutMilliseconds() { /** * boolean value for deciding if the driver should use bulk copy API for batch inserts */ - private boolean useBulkCopyForBatchInsertOnDW; + private boolean useBulkCopyForBatchInsert; - final boolean getUseBulkCopyForBatchInsertOnDW() { - return useBulkCopyForBatchInsertOnDW; + final boolean getUseBulkCopyForBatchInsert() { + return useBulkCopyForBatchInsert; } boolean userSetTNIR = true; @@ -1777,10 +1777,10 @@ else if (0 == requestedPacketSize) setEnablePrepareOnFirstPreparedStatementCall(booleanPropertyOn(sPropKey, sPropValue)); } - sPropKey = SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT_ON_DW.toString(); + sPropKey = SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT.toString(); sPropValue = activeConnectionProperties.getProperty(sPropKey); if (null != sPropValue) { - useBulkCopyForBatchInsertOnDW = booleanPropertyOn(sPropKey, sPropValue); + useBulkCopyForBatchInsert = booleanPropertyOn(sPropKey, sPropValue); } sPropKey = SQLServerDriverStringProperty.SSL_PROTOCOL.toString(); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java index 5cb55e9a1..0c143c293 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java @@ -825,11 +825,11 @@ public int getSocketTimeout() { /** * Setting the use Bulk Copy API for Batch Insert on Azure Data Warehouse boolean * - * @param useBulkCopyForBatchInsertOnDW indicates whether Bulk Copy API should be used for Batch Insert operations. + * @param useBulkCopyForBatchInsert indicates whether Bulk Copy API should be used for Batch Insert operations. */ - public void setUseBulkCopyForBatchInsertOnDW(boolean useBulkCopyForBatchInsertOnDW) { - setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT_ON_DW.toString(), - useBulkCopyForBatchInsertOnDW); + public void setUseBulkCopyForBatchInsert(boolean useBulkCopyForBatchInsert) { + setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT.toString(), + useBulkCopyForBatchInsert); } /** @@ -837,9 +837,9 @@ public void setUseBulkCopyForBatchInsertOnDW(boolean useBulkCopyForBatchInsertOn * * @return whether the driver should use Bulk Copy API for Batch Insert operations. */ - public boolean getUseBulkCopyForBatchInsertOnDW() { - return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT_ON_DW.toString(), - SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT_ON_DW.getDefaultValue()); + public boolean getUseBulkCopyForBatchInsert() { + return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT.toString(), + SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT.getDefaultValue()); } /** diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java index 0ea70f7e4..5f67f93db 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java @@ -354,7 +354,7 @@ enum SQLServerDriverBooleanProperty XOPEN_STATES ("xopenStates", false), FIPS ("fips", false), ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT("enablePrepareOnFirstPreparedStatementCall", SQLServerConnection.DEFAULT_ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT_CALL), - USE_BULK_COPY_FOR_BATCH_INSERT_ON_DW ("useBulkCopyForBatchInsertOnDW", false); + USE_BULK_COPY_FOR_BATCH_INSERT ("useBulkCopyForBatchInsert", false); private final String name; private final boolean defaultValue; @@ -431,7 +431,7 @@ public final class SQLServerDriver implements java.sql.Driver { new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.JAAS_CONFIG_NAME.toString(), SQLServerDriverStringProperty.JAAS_CONFIG_NAME.getDefaultValue(), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.SSL_PROTOCOL.toString(), SQLServerDriverStringProperty.SSL_PROTOCOL.getDefaultValue(), false, new String[] {SSLProtocol.TLS.toString(), SSLProtocol.TLS_V10.toString(), SSLProtocol.TLS_V11.toString(), SSLProtocol.TLS_V12.toString()}), new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.toString(), Integer.toString(SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.getDefaultValue()), false, null), - new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT_ON_DW.toString(), Boolean.toString(SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT_ON_DW.getDefaultValue()),false, TRUE_FALSE), + new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT.toString(), Boolean.toString(SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT.getDefaultValue()), false, TRUE_FALSE), }; // Properties that can only be set by using Properties. diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 9a9dbe7bd..daedef2da 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -110,26 +110,26 @@ private void setPreparedStatementHandle(int handle) { /** * boolean value for deciding if the driver should use bulk copy API for batch inserts */ - private boolean useBulkCopyForBatchInsertOnDW; + private boolean useBulkCopyForBatchInsert; - /** Sets the prepared statement's useBulkCopyForBatchInsertOnDW value. + /** Sets the prepared statement's useBulkCopyForBatchInsert value. * * @return * Per the description. * @throws SQLServerException when an error occurs */ - public boolean getUseBulkCopyForBatchInsertOnDW() throws SQLServerException { + public boolean getUseBulkCopyForBatchInsert() throws SQLServerException { checkClosed(); - return useBulkCopyForBatchInsertOnDW; + return useBulkCopyForBatchInsert; } - /** Fetches the prepared statement's useBulkCopyForBatchInsertOnDW value. + /** Fetches the prepared statement's useBulkCopyForBatchInsert value. * * @throws SQLServerException when an error occurs */ - public void setUseBulkCopyForBatchInsertOnDW(boolean useBulkCopyForBatchInsertOnDW) throws SQLServerException { + public void setUseBulkCopyForBatchInsert(boolean useBulkCopyForBatchInsert) throws SQLServerException { checkClosed(); - this.useBulkCopyForBatchInsertOnDW = useBulkCopyForBatchInsertOnDW; + this.useBulkCopyForBatchInsert = useBulkCopyForBatchInsert; } /** The server handle for this prepared statement. If a value {@literal <} 1 is returned no handle has been created. @@ -2479,24 +2479,24 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL localUserSQL = userSQL; try { - if (isInsert(localUserSQL) && connection.isAzureDW() && (this.useBulkCopyForBatchInsertOnDW || connection.getUseBulkCopyForBatchInsertOnDW())) { + if (isInsert(localUserSQL) && true && (this.useBulkCopyForBatchInsert || connection.getUseBulkCopyForBatchInsert())) { if (batchParamValues == null) { updateCounts = new int[0]; loggerExternal.exiting(getClassNameLogging(), "executeBatch", updateCounts); - return updateCounts; + return updateCounts; } String tableName = parseUserSQLForTableNameDW(false, false); ArrayList columnList = parseUserSQLForColumnListDW(); ArrayList valueList = parseUserSQLForValueListDW(false); - + String destinationTableName = tableName; - SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, - ResultSet.CONCUR_READ_ONLY, connection.getHoldability(), stmtColumnEncriptionSetting); + SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, + connection.getHoldability(), stmtColumnEncriptionSetting); // Get destination metadata try (SQLServerResultSet rs = stmt - .executeQueryInternal("SET FMTONLY ON SELECT * FROM " + destinationTableName + " SET FMTONLY OFF ");) { - + .executeQueryInternal("sp_executesql N'SET FMTONLY ON SELECT * FROM " + destinationTableName + " '");) { + SQLServerBulkBatchInsertRecord batchRecord = new SQLServerBulkBatchInsertRecord(batchParamValues, columnList, valueList, null); for (int i = 1; i <= rs.getColumnCount(); i++) { @@ -2505,24 +2505,25 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL int jdbctype; TypeInfo ti = c.getTypeInfo(); if (null != cryptoMetadata) { - jdbctype = cryptoMetadata.getBaseTypeInfo().getSSType().getJDBCType().getIntValue(); - } else { + jdbctype = cryptoMetadata.getBaseTypeInfo().getSSType().getJDBCType().getIntValue(); + } + else { jdbctype = ti.getSSType().getJDBCType().getIntValue(); } batchRecord.addColumnMetadata(i, c.getColumnName(), jdbctype, ti.getPrecision(), ti.getScale()); } - + SQLServerBulkCopy bcOperation = new SQLServerBulkCopy(connection); bcOperation.setDestinationTableName(tableName); bcOperation.setStmtColumnEncriptionSetting(this.getStmtColumnEncriptionSetting()); + bcOperation.setDestinationTableMetadata(rs); bcOperation.writeToServer((ISQLServerBulkRecord) batchRecord); bcOperation.close(); updateCounts = new int[batchParamValues.size()]; - for (int i = 0; i < batchParamValues.size(); ++i) - { + for (int i = 0; i < batchParamValues.size(); ++i) { updateCounts[i] = 1; } - + batchParamValues = null; loggerExternal.exiting(getClassNameLogging(), "executeBatch", updateCounts); return updateCounts; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index a2d726519..2b6a9d982 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -393,6 +393,6 @@ protected Object[][] getContents() { {"R_invalidSSLProtocol", "SSL Protocol {0} label is not valid. Only TLS, TLSv1, TLSv1.1, and TLSv1.2 are supported."}, {"R_cancelQueryTimeoutPropertyDescription", "The number of seconds to wait to cancel sending a query timeout."}, {"R_invalidCancelQueryTimeout", "The cancel timeout value {0} is not valid."}, - {"R_useBulkCopyForBatchInsertOnDWPropertyDescription", "Whether the driver will use bulk copy API for batch insert operations on Azure Data Warehouse."}, + {"R_useBulkCopyForBatchInsertPropertyDescription", "Whether the driver will use bulk copy API for batch insert operations"}, }; } \ No newline at end of file From f218fe31e8599dee77fde2c6d6ced296a55a6318 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 5 Jun 2018 09:50:03 -0700 Subject: [PATCH 49/84] formatting --- .../jdbc/SQLServerBulkBatchInsertRecord.java | 68 +++++++++++-------- .../sqlserver/jdbc/SQLServerBulkCommon.java | 24 ++++--- .../sqlserver/jdbc/SQLServerBulkCopy.java | 8 +-- .../BatchExecutionWithBulkCopyParseTest.java | 67 ++++++++---------- 4 files changed, 87 insertions(+), 80 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java index 62d990d21..6ec2090f6 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java @@ -22,8 +22,8 @@ import java.util.Set; /** - * A simple implementation of the ISQLServerBulkRecord interface that can be used to read in the basic Java data types from an ArrayList of - * Parameters that were provided by pstmt/cstmt. + * A simple implementation of the ISQLServerBulkRecord interface that can be used to read in the basic Java data types from an ArrayList of Parameters + * that were provided by pstmt/cstmt. */ public class SQLServerBulkBatchInsertRecord extends SQLServerBulkCommon implements ISQLServerBulkRecord, java.lang.AutoCloseable { @@ -32,21 +32,22 @@ public class SQLServerBulkBatchInsertRecord extends SQLServerBulkCommon implemen private ArrayList columnList; private ArrayList valueList; - public SQLServerBulkBatchInsertRecord(ArrayList batchParam, ArrayList columnList, - ArrayList valueList, String encoding) throws SQLServerException { + public SQLServerBulkBatchInsertRecord(ArrayList batchParam, + ArrayList columnList, + ArrayList valueList, + String encoding) throws SQLServerException { loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkBatchInsertRecord"; loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); - loggerExternal.entering(loggerClassName, "SQLServerBulkBatchInsertRecord", - new Object[] {batchParam, encoding}); - + loggerExternal.entering(loggerClassName, "SQLServerBulkBatchInsertRecord", new Object[] {batchParam, encoding}); + if (null == batchParam) { throwInvalidArgument("batchParam"); } - + if (null == valueList) { throwInvalidArgument("valueList"); } - + this.batchParam = batchParam; this.columnList = columnList; this.valueList = valueList; @@ -100,26 +101,29 @@ public boolean isAutoIncrement(int column) { @Override public Object[] getRowData() throws SQLServerException { - + Object[] data = new Object[columnMetadata.size()]; - + if (null == columnList || columnList.size() == 0) { int valueIndex = 0; for (int i = 0; i < data.length; i++) { if (valueList.get(i).equalsIgnoreCase("?")) { data[i] = batchParam.get(batchParamIndex)[valueIndex].getSetterValue(); valueIndex++; - } else { + } + else { // remove 's at the beginning and end of the value, if it exists. int len = valueList.get(i).length(); if (valueList.get(i).charAt(0) == '\'' && valueList.get(i).charAt(len - 1) == '\'') { data[i] = valueList.get(i).substring(1, len - 1); - } else { + } + else { data[i] = valueList.get(i); } } } - } else { + } + else { int valueIndex = 0; int columnListIndex = 0; for (int i = 0; i < data.length; i++) { @@ -127,17 +131,20 @@ public Object[] getRowData() throws SQLServerException { if (valueList.get(i).equalsIgnoreCase("?")) { data[i] = batchParam.get(i)[valueIndex].getSetterValue(); valueIndex++; - } else { + } + else { // remove 's at the beginning and end of the value, if it exists. int len = valueList.get(i).length(); if (valueList.get(i).charAt(0) == '\'' && valueList.get(i).charAt(len - 1) == '\'') { data[i] = valueList.get(i).substring(1, len - 1); - } else { + } + else { data[i] = valueList.get(i); } } columnListIndex++; - } else { + } + else { data[i] = ""; } } @@ -170,8 +177,8 @@ public Object[] getRowData() throws SQLServerException { switch (cm.columnType) { /* - * Both BCP and BULK INSERT considers double quotes as part of the data and throws error if any data (say "10") is to be - * inserted into an numeric column. Our implementation does the same. + * Both BCP and BULK INSERT considers double quotes as part of the data and throws error if any data (say "10") is to be inserted + * into an numeric column. Our implementation does the same. */ case Types.INTEGER: { // Formatter to remove the decimal part as SQL Server floors the decimal in integer types @@ -194,10 +201,11 @@ public Object[] getRowData() throws SQLServerException { BigDecimal bd = new BigDecimal(data[pair.getKey() - 1].toString().trim()); try { dataRow[pair.getKey() - 1] = bd.setScale(0, RoundingMode.DOWN).longValueExact(); - } catch (ArithmeticException ex) { + } + 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, ex); + throw new SQLServerException(form.format(new Object[] {value, JDBCType.of(cm.columnType)}), null, 0, ex); } break; } @@ -214,7 +222,8 @@ public Object[] getRowData() throws SQLServerException { // Any non-zero value (integer/double) => 1, 0/0.0 => 0 try { dataRow[pair.getKey() - 1] = (0 == Double.parseDouble(data[pair.getKey() - 1].toString())) ? Boolean.FALSE : Boolean.TRUE; - } catch (NumberFormatException e) { + } + catch (NumberFormatException e) { dataRow[pair.getKey() - 1] = Boolean.parseBoolean(data[pair.getKey() - 1].toString()); } break; @@ -238,7 +247,8 @@ public Object[] getRowData() throws SQLServerException { String binData = data[pair.getKey() - 1].toString().trim(); if (binData.startsWith("0x") || binData.startsWith("0X")) { dataRow[pair.getKey() - 1] = binData.substring(2); - } else { + } + else { dataRow[pair.getKey() - 1] = binData; } break; @@ -297,18 +307,20 @@ else if (dateTimeFormatter != null) break; } } - } catch (IllegalArgumentException e) { + } + 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, e); - } catch (ArrayIndexOutOfBoundsException e) { + throw new SQLServerException(form.format(new Object[] {value, JDBCType.of(cm.columnType)}), null, 0, e); + } + catch (ArrayIndexOutOfBoundsException e) { throw new SQLServerException(SQLServerException.getErrString("R_CSVDataSchemaMismatch"), e); } - + } return dataRow; } - + @Override public boolean next() throws SQLServerException { batchParamIndex++; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java index fc4c79679..669845b90 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java @@ -14,7 +14,7 @@ import java.util.Map.Entry; abstract class SQLServerBulkCommon { - + /* * Class to represent the column metadata */ @@ -37,25 +37,25 @@ protected class ColumnMetadata { this.dateTimeFormatter = dateTimeFormatter; } } - + /* * Class name for logging. */ protected static String loggerClassName; - + /* * Logger */ protected java.util.logging.Logger loggerExternal; - + /* * Contains all the column names if firstLineIsColumnNames is true */ protected String[] columnNames = null; - + /* - * Metadata to represent the columns in the batch/file. Each column should be mapped to its corresponding position within the parameter (from position 1 and - * onwards) + * Metadata to represent the columns in the batch/file. Each column should be mapped to its corresponding position within the parameter (from + * position 1 and onwards) */ protected Map columnMetadata; @@ -178,9 +178,11 @@ else if ((columnNames != null) && (columnNames.length >= positionInSource)) case microsoft.sql.Types.DATETIMEOFFSET: if (this instanceof SQLServerBulkCSVFileRecord) { columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, 50, scale, dateTimeFormatter)); - } else if (this instanceof SQLServerBulkBatchInsertRecord) { + } + else if (this instanceof SQLServerBulkBatchInsertRecord) { columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, precision, scale, dateTimeFormatter)); - } else { + } + else { columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, 50, scale, dateTimeFormatter)); } break; @@ -266,7 +268,7 @@ public void setTimeWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); } - + /* * Helper method to throw a SQLServerExeption with the invalidArgument message and given argument. */ @@ -275,7 +277,7 @@ protected void throwInvalidArgument(String argument) throws SQLServerException { Object[] msgArgs = {argument}; SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, false); } - + /* * Method to throw a SQLServerExeption for duplicate column names */ diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java index ba36cbda4..ab2e62449 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java @@ -1748,16 +1748,16 @@ private void getDestinationMetadata() throws SQLServerException { SQLServerStatement stmt = null; try { - if (null == destinationTableMetadata) { + if (null != destinationTableMetadata) { + rs = (SQLServerResultSet) destinationTableMetadata; + } + else { stmt = (SQLServerStatement) connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, connection.getHoldability(), stmtColumnEncriptionSetting); // Get destination metadata rs = stmt.executeQueryInternal("sp_executesql N'SET FMTONLY ON SELECT * FROM " + destinationTableName + " '"); } - else { - rs = (SQLServerResultSet) destinationTableMetadata; - } destColumnCount = rs.getMetaData().getColumnCount(); destColumnMetadata = new HashMap<>(); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyParseTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyParseTest.java index 79a43c5e6..1a42eea3d 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyParseTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyParseTest.java @@ -10,7 +10,6 @@ import java.lang.reflect.Method; import java.sql.Connection; import java.sql.DriverManager; -import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; @@ -27,22 +26,20 @@ import com.microsoft.sqlserver.testframework.DBConnection; import com.microsoft.sqlserver.testframework.Utils; - @RunWith(JUnitPlatform.class) public class BatchExecutionWithBulkCopyParseTest extends AbstractTest { static SQLServerPreparedStatement pstmt = null; static Statement stmt = null; static Connection connection = null; - + @Test - public void testIsInsert() throws SQLException, NoSuchMethodException, SecurityException, - IllegalAccessException, IllegalArgumentException, InvocationTargetException { + public void testIsInsert() throws SQLException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { String valid1 = "INSERT INTO PeterTable values (1, 2)"; String valid2 = " INSERT INTO PeterTable values (1, 2)"; String valid3 = "/* asdf */ INSERT INTO PeterTable values (1, 2)"; String invalid = "Select * from PEterTable"; - + stmt = connection.createStatement(); Method method = stmt.getClass().getDeclaredMethod("isInsert", String.class); method.setAccessible(true); @@ -51,108 +48,104 @@ public void testIsInsert() throws SQLException, NoSuchMethodException, SecurityE assertTrue((boolean) method.invoke(stmt, valid3)); assertFalse((boolean) method.invoke(stmt, invalid)); } - + @Test - public void testComments() throws SQLException, NoSuchFieldException, SecurityException, - IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + public void testComments() throws SQLException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { pstmt = (SQLServerPreparedStatement) connection.prepareStatement(""); - + String valid = "/* rando comment *//* rando comment */ INSERT /* rando comment */ INTO /* rando comment *//*rando comment*/ PeterTable /*rando comment */" + " /* rando comment */values/* rando comment */ (1, 2)"; - + Field f1 = pstmt.getClass().getSuperclass().getDeclaredField("localUserSQL"); f1.setAccessible(true); f1.set(pstmt, valid); Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class); method.setAccessible(true); - + assertEquals((String) method.invoke(pstmt, false, false), "PeterTable"); } - + @Test - public void testBrackets() throws SQLException, NoSuchFieldException, SecurityException, - IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + public void testBrackets() throws SQLException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { pstmt = (SQLServerPreparedStatement) connection.prepareStatement(""); - + String valid = "/* rando comment *//* rando comment */ INSERT /* rando comment */ INTO /* rando comment *//*rando comment*/ [Peter[]]Table] /*rando comment */" + " /* rando comment */values/* rando comment */ (1, 2)"; - + Field f1 = pstmt.getClass().getSuperclass().getDeclaredField("localUserSQL"); f1.setAccessible(true); f1.set(pstmt, valid); Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class); method.setAccessible(true); - + assertEquals((String) method.invoke(pstmt, false, false), "Peter[]Table"); } - + @Test - public void testDoubleQuotes() throws SQLException, NoSuchFieldException, SecurityException, - IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + public void testDoubleQuotes() throws SQLException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { pstmt = (SQLServerPreparedStatement) connection.prepareStatement(""); - + String valid = "/* rando comment *//* rando comment */ INSERT /* rando comment */ INTO /* rando comment *//*rando comment*/ \"Peter\"\"\"\"Table\" /*rando comment */" + " /* rando comment */values/* rando comment */ (1, 2)"; - + Field f1 = pstmt.getClass().getSuperclass().getDeclaredField("localUserSQL"); f1.setAccessible(true); f1.set(pstmt, valid); Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class); method.setAccessible(true); - + assertEquals((String) method.invoke(pstmt, false, false), "Peter\"\"Table"); } - + @Test - public void testAll() throws SQLException, NoSuchFieldException, SecurityException, - IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + public void testAll() throws SQLException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { pstmt = (SQLServerPreparedStatement) connection.prepareStatement(""); - + String valid = "/* rando comment *//* rando comment */ INSERT /* rando comment */ INTO /* rando comment *//*rando comment*/ \"Peter\"\"\"\"Table\" /*rando comment */" + " /* rando comment */ (\"c1\"/* rando comment */, /* rando comment */[c2]/* rando comment */, /* rando comment */ /* rando comment */c3/* rando comment */, c4)" + "values/* rando comment */ (/* rando comment */1/* rando comment */, /* rando comment */2/* rando comment */ , '?', ?)/* rando comment */"; - + Field f1 = pstmt.getClass().getSuperclass().getDeclaredField("localUserSQL"); f1.setAccessible(true); f1.set(pstmt, valid); Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class); method.setAccessible(true); - + assertEquals((String) method.invoke(pstmt, false, false), "Peter\"\"Table"); - + method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForColumnListDW"); method.setAccessible(true); - + ArrayList columnList = (ArrayList) method.invoke(pstmt); ArrayList columnListExpected = new ArrayList(); columnListExpected.add("c1"); columnListExpected.add("c2"); columnListExpected.add("c3"); columnListExpected.add("c4"); - + for (int i = 0; i < columnListExpected.size(); i++) { assertEquals(columnList.get(i), columnListExpected.get(i)); } - + method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForValueListDW", boolean.class); method.setAccessible(true); - + ArrayList valueList = (ArrayList) method.invoke(pstmt, false); ArrayList valueListExpected = new ArrayList(); valueListExpected.add("1"); valueListExpected.add("2"); valueListExpected.add("'?'"); valueListExpected.add("?"); - + for (int i = 0; i < valueListExpected.size(); i++) { assertEquals(valueList.get(i), valueListExpected.get(i)); } } - + @BeforeEach public void testSetup() throws TestAbortedException, Exception { assumeTrue(13 <= new DBConnection(connectionString).getServerVersion(), From a8dcc9e4797df5ecd1658d076372588a52d5e1e2 Mon Sep 17 00:00:00 2001 From: rene-ye Date: Wed, 6 Jun 2018 10:11:33 -0700 Subject: [PATCH 50/84] fix for getSchema when using "-" in name --- .../jdbc/SQLServerDatabaseMetaData.java | 5 +- .../DatabaseMetaDataTest.java | 104 ++++++++++++++++++ 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 3f3ed20f8..71729ff8b 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -1177,8 +1177,9 @@ private java.sql.ResultSet getSchemasInternal(String catalog, String schema = "sys.schemas"; String schemaName = "sys.schemas.name"; if (null != catalog && catalog.length() != 0) { - schema = catalog + "." + schema; - schemaName = catalog + "." + schemaName; + final String catalogId = Util.escapeSQLId(catalog); + schema = catalogId + "." + schema; + schemaName = catalogId + "." + schemaName; } // The common schemas need to be under null catalog name however the schemas specific to the particular catalog has to have the current 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 515dd9d62..1e5599e99 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -7,8 +7,10 @@ */ package com.microsoft.sqlserver.jdbc.databasemetadata; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -23,6 +25,8 @@ import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; +import java.util.UUID; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.text.MessageFormat; @@ -175,6 +179,106 @@ public void testDBSchema() throws SQLException { assertTrue(!StringUtils.isEmpty(rs.getString(1)), form.format(msgArgs)); } } + + /** + * Tests that the catalog parameter containing - is escaped by + * {@link SQLServerDatabaseMetaData#getSchemas(String catalog, String schemaPattern)}. + * @throws SQLException + */ + @Test + public void testDBSchemasForDashedCatalogName() throws SQLException { + final UUID id = UUID.randomUUID(); + final String testCatalog = "dash-catalog"+id; + final String testSchema = "some-schema"+id; + boolean dropDatabase = false; + + try (final Connection dashConn = DriverManager.getConnection(connectionString); + final Statement dashStatement = dashConn.createStatement()) { + + connection.createStatement().execute(String.format("CREATE DATABASE [%s]", testCatalog)); + dropDatabase = true; + dashStatement.execute(String.format("USE [%s]", testCatalog)); + dashStatement.execute(String.format("CREATE SCHEMA [%s]", testSchema)); + + final DatabaseMetaData databaseMetaData = connection.getMetaData(); + final ResultSet rs = databaseMetaData.getSchemas(testCatalog, null); + + final MessageFormat schemaEmptyFormat = new MessageFormat(TestResource.getResource("R_nameEmpty")); + final Object[] schemaMsgArgs = {"Schema"}; + + boolean hasResults = false; + boolean hasDashCatalogSchema = false; + while (rs.next()) { + hasResults = true; + final String schemaName = rs.getString(1); + assertTrue(!StringUtils.isEmpty(schemaName), schemaEmptyFormat.format(schemaMsgArgs)); + final String catalogName = rs.getString(2); + if (schemaName.equals(testSchema)) { + hasDashCatalogSchema = true; + assertEquals(catalogName, testCatalog); + } else { + assertNull(catalogName); + } + } + + final MessageFormat atLeastOneFoundFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); + assertTrue(hasResults, atLeastOneFoundFormat.format(schemaMsgArgs)); + + final MessageFormat dashCatalogFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); + assertTrue(hasDashCatalogSchema, dashCatalogFormat.format(new Object[] {testSchema})); + } finally { + if (dropDatabase) { + connection.createStatement().execute(String.format("DROP DATABASE [%s]", testCatalog)); + } + } + } + + /** + * Tests that the catalog parameter containing - is escaped by + * {@link SQLServerDatabaseMetaData#getSchemas(String catalog, String schemaPattern)}. + * @throws SQLException + */ + @Test + public void testDBSchemasForDashedCatalogNameWithPattern() throws SQLException { + final UUID id = UUID.randomUUID(); + final String testCatalog = "dash-catalog"+id; + final String testSchema = "some-schema"+id; + boolean dropDatabase = false; + + try (final Connection dashConn = DriverManager.getConnection(connectionString); + final Statement dashStatement = dashConn.createStatement()) { + + connection.createStatement().execute(String.format("CREATE DATABASE [%s]", testCatalog)); + dropDatabase = true; + dashStatement.execute(String.format("USE [%s]", testCatalog)); + dashStatement.execute(String.format("CREATE SCHEMA [%s]", testSchema)); + + final DatabaseMetaData databaseMetaData = connection.getMetaData(); + final ResultSet rs = databaseMetaData.getSchemas(testCatalog, "some-%"); + + final MessageFormat schemaEmptyFormat = new MessageFormat(TestResource.getResource("R_nameEmpty")); + final Object[] schemaMsgArgs = {testSchema}; + final Object[] catalogMsgArgs = {testCatalog}; + + boolean hasResults = false; + while (rs.next()) { + hasResults = true; + final String schemaName = rs.getString(1); + final String catalogName = rs.getString(2); + assertTrue(!StringUtils.isEmpty(schemaName), schemaEmptyFormat.format(schemaMsgArgs)); + assertTrue(!StringUtils.isEmpty(catalogName), schemaEmptyFormat.format(catalogMsgArgs)); + assertEquals(schemaName, schemaMsgArgs[0]); + assertEquals(catalogName, catalogMsgArgs[0]); + } + + final MessageFormat atLeastOneFoundFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); + assertTrue(hasResults, atLeastOneFoundFormat.format(schemaMsgArgs)); + } finally { + if (dropDatabase) { + connection.createStatement().execute(String.format("DROP DATABASE [%s]", testCatalog)); + } + } + } /** * Get All Tables. From 3c5d02340133181fe67983ecb70000638d1a33d7 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Wed, 6 Jun 2018 16:26:26 -0700 Subject: [PATCH 51/84] Reformatting + adding more tests --- .../jdbc/SQLServerBulkBatchInsertRecord.java | 358 +++++++++--------- .../jdbc/SQLServerPreparedStatement.java | 3 +- ...va => BatchExecutionWithBulkCopyTest.java} | 158 +++++++- .../preparedStatement/RegressionTest.java | 8 +- 4 files changed, 331 insertions(+), 196 deletions(-) rename src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/{BatchExecutionWithBulkCopyParseTest.java => BatchExecutionWithBulkCopyTest.java} (60%) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java index 6ec2090f6..a355d45e5 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java @@ -99,226 +99,216 @@ public boolean isAutoIncrement(int column) { return false; } - @Override - public Object[] getRowData() throws SQLServerException { + private Object convertValue(ColumnMetadata cm, + Object data) throws SQLServerException { + switch (cm.columnType) { + /* + * Both BCP and BULK INSERT considers double quotes as part of the data and throws error if any data (say "10") is to be inserted into an + * numeric column. Our implementation does the same. + */ + case Types.INTEGER: { + // Formatter to remove the decimal part as SQL Server floors the decimal in integer types + DecimalFormat decimalFormatter = new DecimalFormat("#"); + String formatedfInput = decimalFormatter.format(Double.parseDouble(data.toString())); + return Integer.valueOf(formatedfInput); + } - Object[] data = new Object[columnMetadata.size()]; + case Types.TINYINT: + case Types.SMALLINT: { + // Formatter to remove the decimal part as SQL Server floors the decimal in integer types + DecimalFormat decimalFormatter = new DecimalFormat("#"); + String formatedfInput = decimalFormatter.format(Double.parseDouble(data.toString())); + return Short.valueOf(formatedfInput); + } - if (null == columnList || columnList.size() == 0) { - int valueIndex = 0; - for (int i = 0; i < data.length; i++) { - if (valueList.get(i).equalsIgnoreCase("?")) { - data[i] = batchParam.get(batchParamIndex)[valueIndex].getSetterValue(); - valueIndex++; + case Types.BIGINT: { + BigDecimal bd = new BigDecimal(data.toString().trim()); + try { + return bd.setScale(0, RoundingMode.DOWN).longValueExact(); } - else { - // remove 's at the beginning and end of the value, if it exists. - int len = valueList.get(i).length(); - if (valueList.get(i).charAt(0) == '\'' && valueList.get(i).charAt(len - 1) == '\'') { - data[i] = valueList.get(i).substring(1, len - 1); - } - else { - data[i] = valueList.get(i); - } + catch (ArithmeticException ex) { + String value = "'" + data + "'"; + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_errorConvertingValue")); + throw new SQLServerException(form.format(new Object[] {value, JDBCType.of(cm.columnType)}), null, 0, ex); } } - } - else { - int valueIndex = 0; - int columnListIndex = 0; - for (int i = 0; i < data.length; i++) { - if (columnList.size() > columnListIndex && columnList.get(columnListIndex).equals(columnMetadata.get(i + 1).columnName)) { - if (valueList.get(i).equalsIgnoreCase("?")) { - data[i] = batchParam.get(i)[valueIndex].getSetterValue(); - valueIndex++; - } - else { - // remove 's at the beginning and end of the value, if it exists. - int len = valueList.get(i).length(); - if (valueList.get(i).charAt(0) == '\'' && valueList.get(i).charAt(len - 1) == '\'') { - data[i] = valueList.get(i).substring(1, len - 1); - } - else { - data[i] = valueList.get(i); - } - } - columnListIndex++; + + case Types.DECIMAL: + case Types.NUMERIC: { + BigDecimal bd = new BigDecimal(data.toString().trim()); + return bd.setScale(cm.scale, RoundingMode.HALF_UP); + } + + case Types.BIT: { + // "true" => 1, "false" => 0 + // Any non-zero value (integer/double) => 1, 0/0.0 => 0 + try { + return (0 == Double.parseDouble(data.toString())) ? Boolean.FALSE : Boolean.TRUE; } - else { - data[i] = ""; + catch (NumberFormatException e) { + return Boolean.parseBoolean(data.toString()); } } - } - - // Cannot go directly from String[] to Object[] and expect it to act as an array. - Object[] dataRow = new Object[data.length]; - - for (Entry pair : columnMetadata.entrySet()) { - ColumnMetadata cm = pair.getValue(); - if (data.length < pair.getKey() - 1) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumn")); - Object[] msgArgs = {pair.getKey()}; - throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); + case Types.REAL: { + return Float.parseFloat(data.toString()); } - // Source header has more columns than current param read - if (columnNames != null && (columnNames.length > data.length)) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_CSVDataSchemaMismatch")); - Object[] msgArgs = {}; - throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); + case Types.DOUBLE: { + return Double.parseDouble(data.toString()); } - try { - if (0 == data[pair.getKey() - 1].toString().length()) { - dataRow[pair.getKey() - 1] = null; - continue; + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + case Types.BLOB: { + // Strip off 0x if present. + String binData = data.toString().trim(); + if (binData.startsWith("0x") || binData.startsWith("0X")) { + return binData.substring(2); + } + else { + return binData; } + } - switch (cm.columnType) { - /* - * Both BCP and BULK INSERT considers double quotes as part of the data and throws error if any data (say "10") is to be inserted - * into an numeric column. Our implementation does the same. - */ - case Types.INTEGER: { - // Formatter to remove the decimal part as SQL Server floors the decimal in integer types - DecimalFormat decimalFormatter = new DecimalFormat("#"); - String formatedfInput = decimalFormatter.format(Double.parseDouble(data[pair.getKey() - 1].toString())); - dataRow[pair.getKey() - 1] = Integer.valueOf(formatedfInput); - break; - } + case 2013: // java.sql.Types.TIME_WITH_TIMEZONE + { + DriverJDBCVersion.checkSupportsJDBC42(); + OffsetTime offsetTimeValue; - case Types.TINYINT: - case Types.SMALLINT: { - // Formatter to remove the decimal part as SQL Server floors the decimal in integer types - DecimalFormat decimalFormatter = new DecimalFormat("#"); - String formatedfInput = decimalFormatter.format(Double.parseDouble(data[pair.getKey() - 1].toString())); - dataRow[pair.getKey() - 1] = Short.valueOf(formatedfInput); - break; - } + // The per-column DateTimeFormatter gets priority. + if (null != cm.dateTimeFormatter) + offsetTimeValue = OffsetTime.parse(data.toString(), cm.dateTimeFormatter); + else if (timeFormatter != null) + offsetTimeValue = OffsetTime.parse(data.toString(), timeFormatter); + else + offsetTimeValue = OffsetTime.parse(data.toString()); - case Types.BIGINT: { - BigDecimal bd = new BigDecimal(data[pair.getKey() - 1].toString().trim()); - try { - dataRow[pair.getKey() - 1] = bd.setScale(0, RoundingMode.DOWN).longValueExact(); - } - 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, ex); - } - break; - } + return offsetTimeValue; + } - case Types.DECIMAL: - case Types.NUMERIC: { - BigDecimal bd = new BigDecimal(data[pair.getKey() - 1].toString().trim()); - dataRow[pair.getKey() - 1] = bd.setScale(cm.scale, RoundingMode.HALF_UP); - break; - } + case 2014: // java.sql.Types.TIMESTAMP_WITH_TIMEZONE + { + DriverJDBCVersion.checkSupportsJDBC42(); + OffsetDateTime offsetDateTimeValue; - case Types.BIT: { - // "true" => 1, "false" => 0 - // Any non-zero value (integer/double) => 1, 0/0.0 => 0 - try { - dataRow[pair.getKey() - 1] = (0 == Double.parseDouble(data[pair.getKey() - 1].toString())) ? Boolean.FALSE : Boolean.TRUE; - } - catch (NumberFormatException e) { - dataRow[pair.getKey() - 1] = Boolean.parseBoolean(data[pair.getKey() - 1].toString()); - } - break; - } + // The per-column DateTimeFormatter gets priority. + if (null != cm.dateTimeFormatter) + offsetDateTimeValue = OffsetDateTime.parse(data.toString(), cm.dateTimeFormatter); + else if (dateTimeFormatter != null) + offsetDateTimeValue = OffsetDateTime.parse(data.toString(), dateTimeFormatter); + else + offsetDateTimeValue = OffsetDateTime.parse(data.toString()); - case Types.REAL: { - dataRow[pair.getKey() - 1] = Float.parseFloat(data[pair.getKey() - 1].toString()); - break; - } + return offsetDateTimeValue; + } - case Types.DOUBLE: { - dataRow[pair.getKey() - 1] = Double.parseDouble(data[pair.getKey() - 1].toString()); - break; - } + case Types.NULL: { + return null; + } - case Types.BINARY: - case Types.VARBINARY: - case Types.LONGVARBINARY: - case Types.BLOB: { - // Strip off 0x if present. - String binData = data[pair.getKey() - 1].toString().trim(); - if (binData.startsWith("0x") || binData.startsWith("0X")) { - dataRow[pair.getKey() - 1] = binData.substring(2); - } - else { - dataRow[pair.getKey() - 1] = binData; - } - break; - } + case Types.DATE: + case Types.CHAR: + case Types.NCHAR: + case Types.VARCHAR: + case Types.NVARCHAR: + case Types.LONGVARCHAR: + case Types.LONGNVARCHAR: + case Types.CLOB: + default: { + // The string is copied as is. + return data; + } + } + } - case 2013: // java.sql.Types.TIME_WITH_TIMEZONE - { - DriverJDBCVersion.checkSupportsJDBC42(); - OffsetTime offsetTimeValue; - - // The per-column DateTimeFormatter gets priority. - if (null != cm.dateTimeFormatter) - offsetTimeValue = OffsetTime.parse(data[pair.getKey() - 1].toString(), cm.dateTimeFormatter); - else if (timeFormatter != null) - offsetTimeValue = OffsetTime.parse(data[pair.getKey() - 1].toString(), timeFormatter); - else - offsetTimeValue = OffsetTime.parse(data[pair.getKey() - 1].toString()); - - dataRow[pair.getKey() - 1] = offsetTimeValue; - break; - } + private String removeSingleQuote(String s) { + int len = s.length(); + return (s.charAt(0) == '\'' && s.charAt(len - 1) == '\'') ? s.substring(1, len - 1) : s; + } - case 2014: // java.sql.Types.TIMESTAMP_WITH_TIMEZONE - { - DriverJDBCVersion.checkSupportsJDBC42(); - OffsetDateTime offsetDateTimeValue; - - // The per-column DateTimeFormatter gets priority. - if (null != cm.dateTimeFormatter) - offsetDateTimeValue = OffsetDateTime.parse(data[pair.getKey() - 1].toString(), cm.dateTimeFormatter); - else if (dateTimeFormatter != null) - offsetDateTimeValue = OffsetDateTime.parse(data[pair.getKey() - 1].toString(), dateTimeFormatter); - else - offsetDateTimeValue = OffsetDateTime.parse(data[pair.getKey() - 1].toString()); - - dataRow[pair.getKey() - 1] = offsetDateTimeValue; - break; + @Override + public Object[] getRowData() throws SQLServerException { + Object[] data = new Object[columnMetadata.size()]; + int valueIndex = 0; + String valueData; + Object rowData; + int columnListIndex = 0; + + // check if the size of the list of values = size of the list of columns (which is optional) + if (null != columnList && columnList.size() != valueList.size()) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_CSVDataSchemaMismatch")); + Object[] msgArgs = {}; + throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); + } + + for (Entry pair : columnMetadata.entrySet()) { + int index = pair.getKey() - 1; + + // To explain what each variable represents: + // columnMetadata = map containing the ENTIRE list of columns in the table. + // columnList = the *optional* list of columns the user can provide. For example, the (c1, c3) part of this query: INSERT into t1 (c1, c3) values (?, ?) + // valueList = the *mandatory* list of columns the user needs provide. This is the (?, ?) part of the previous query. The size of this valueList will always equal the number of + // the entire columns in the table IF columnList has NOT been provided. If columnList HAS been provided, then this valueList may be smaller than the list of all columns (which is columnMetadata). + + // case when the user has not provided the optional list of column names. + if (null == columnList || columnList.size() == 0) { + valueData = valueList.get(index); + // if the user has provided a wildcard for this column, fetch the set value from the batchParam. + if (valueData.equalsIgnoreCase("?")) { + rowData = batchParam.get(batchParamIndex)[valueIndex++].getSetterValue(); + } + else if (valueData.equalsIgnoreCase("null")) { + rowData = ""; + } + // if the user has provided a hardcoded value for this column, rowData is simply set to the hardcoded value. + else { + rowData = removeSingleQuote(valueData); + } + } + // case when the user has provided the optional list of column names. + else { + // columnListIndex is a separate counter we need to keep track of for each time we've processed a column + // that the user provided. + // for example, if the user provided an optional columnList of (c1, c3, c5, c7) in a table that has 8 columns (c1~c8), + // then the columnListIndex would increment only when we're dealing with the four columns inside columnMetadata. + // compare the list of the optional list of column names to the table's metadata, and match each other, so we assign the correct value to each column. + if (columnList.size() > columnListIndex && columnList.get(columnListIndex).equalsIgnoreCase(columnMetadata.get(index + 1).columnName)) { + valueData = valueList.get(columnListIndex); + if (valueData.equalsIgnoreCase("?")) { + rowData = batchParam.get(batchParamIndex)[valueIndex++].getSetterValue(); } - - case Types.NULL: { - dataRow[pair.getKey() - 1] = null; - break; + else if (valueData.equalsIgnoreCase("null")) { + rowData = ""; } - - case Types.DATE: - case Types.CHAR: - case Types.NCHAR: - case Types.VARCHAR: - case Types.NVARCHAR: - case Types.LONGVARCHAR: - case Types.LONGNVARCHAR: - case Types.CLOB: - default: { - // The string is copied as is. - dataRow[pair.getKey() - 1] = data[pair.getKey() - 1]; - break; + else { + rowData = removeSingleQuote(valueData); } + columnListIndex++; } + else { + rowData = ""; + } + } + + try { + if (0 == rowData.toString().length()) { + data[index] = null; + continue; + } + data[index] = convertValue(pair.getValue(), rowData); } catch (IllegalArgumentException e) { - String value = "'" + data[pair.getKey() - 1] + "'"; + String value = "'" + rowData + "'"; MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_errorConvertingValue")); - throw new SQLServerException(form.format(new Object[] {value, JDBCType.of(cm.columnType)}), null, 0, e); + throw new SQLServerException(form.format(new Object[] {value, JDBCType.of(pair.getValue().columnType)}), null, 0, e); } catch (ArrayIndexOutOfBoundsException e) { throw new SQLServerException(SQLServerException.getErrString("R_CSVDataSchemaMismatch"), e); } - } - return dataRow; + return data; } @Override diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index daedef2da..46a43f8a7 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -2479,7 +2479,7 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL localUserSQL = userSQL; try { - if (isInsert(localUserSQL) && true && (this.useBulkCopyForBatchInsert || connection.getUseBulkCopyForBatchInsert())) { + if (isInsert(localUserSQL) && connection.isAzureDW() && (this.useBulkCopyForBatchInsert || connection.getUseBulkCopyForBatchInsert())) { if (batchParamValues == null) { updateCounts = new int[0]; loggerExternal.exiting(getClassNameLogging(), "executeBatch", updateCounts); @@ -2547,7 +2547,6 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL } } - if (batchParamValues == null) updateCounts = new int[0]; else diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyParseTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java similarity index 60% rename from src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyParseTest.java rename to src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java index 1a42eea3d..5335f4401 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyParseTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java @@ -9,17 +9,23 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Connection; +import java.sql.Date; import java.sql.DriverManager; +import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.sql.Timestamp; import java.util.ArrayList; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; import org.opentest4j.TestAbortedException; +import com.microsoft.sqlserver.jdbc.SQLServerConnection; import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; import com.microsoft.sqlserver.jdbc.SQLServerStatement; import com.microsoft.sqlserver.testframework.AbstractTest; @@ -27,11 +33,12 @@ import com.microsoft.sqlserver.testframework.Utils; @RunWith(JUnitPlatform.class) -public class BatchExecutionWithBulkCopyParseTest extends AbstractTest { +public class BatchExecutionWithBulkCopyTest extends AbstractTest { static SQLServerPreparedStatement pstmt = null; static Statement stmt = null; static Connection connection = null; + static String tableName = "BulkCopyParseTest" + System.currentTimeMillis(); @Test public void testIsInsert() throws SQLException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { @@ -145,17 +152,154 @@ public void testAll() throws SQLException, NoSuchFieldException, SecurityExcepti assertEquals(valueList.get(i), valueListExpected.get(i)); } } + + @Test + public void testAllcolumns() throws Exception { + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); + f1.setAccessible(true); + f1.set(connection, true); + + String valid = "INSERT INTO " + tableName + " values " + + "(" + + "?, " + + "?, " + + "?, " + + "?, " + + "?, " + + "?, " + + "?, " + + "?, " + + "?, " + + ")"; + + SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); + SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); + + Timestamp myTimestamp = new Timestamp(114550L); + + Date d = new Date(114550L); + + pstmt.setInt(1, 1234); + pstmt.setBoolean(2, false); + pstmt.setString(3, "a"); + pstmt.setDate(4, d); + pstmt.setDateTime(5, myTimestamp); + pstmt.setFloat(6, (float) 123.45); + pstmt.setString(7, "b"); + pstmt.setString(8, "varc"); + pstmt.setString(9, "varcmax"); + pstmt.addBatch(); + + pstmt.executeBatch(); + + ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName); + + Object[] expected = new Object[9]; + + expected[0] = 1234; + expected[1] = false; + expected[2] = "a"; + expected[3] = d; + expected[4] = myTimestamp; + expected[5] = 123.45; + expected[6] = "b"; + expected[7] = "varc"; + expected[8] = "varcmax"; + + rs.next(); + for (int i=0; i < expected.length; i++) { + assertEquals(rs.getObject(i + 1).toString(), expected[i].toString()); + } + } + @Test + public void testMixColumns() throws Exception { + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); + f1.setAccessible(true); + f1.set(connection, true); + + String valid = "INSERT INTO " + tableName + " (c1, c3, c5, c8) values " + + "(" + + "?, " + + "?, " + + "?, " + + "?, " + + ")"; + + SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); + SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); + + Timestamp myTimestamp = new Timestamp(114550L); + + Date d = new Date(114550L); + + pstmt.setInt(1, 1234); + pstmt.setString(2, "a"); + pstmt.setDateTime(3, myTimestamp); + pstmt.setString(4, "varc"); + pstmt.addBatch(); + + pstmt.executeBatch(); + + ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName); + + Object[] expected = new Object[9]; + + expected[0] = 1234; + expected[1] = false; + expected[2] = "a"; + expected[3] = d; + expected[4] = myTimestamp; + expected[5] = 123.45; + expected[6] = "b"; + expected[7] = "varc"; + expected[8] = "varcmax"; + + rs.next(); + for (int i=0; i < expected.length; i++) { + if (null != rs.getObject(i + 1)) { + assertEquals(rs.getObject(i + 1).toString(), expected[i].toString()); + } + } + } @BeforeEach public void testSetup() throws TestAbortedException, Exception { - assumeTrue(13 <= new DBConnection(connectionString).getServerVersion(), - "Aborting test case as SQL Server version is not compatible with Always encrypted "); - - connection = DriverManager.getConnection(connectionString); + connection = DriverManager.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); - Utils.dropTableIfExists("esimple", stmt); - String sql1 = "create table esimple (id integer not null, name varchar(255), constraint pk_esimple primary key (id))"; + + Utils.dropTableIfExists(tableName, stmt); + String sql1 = "create table " + tableName + " " + + "(" + + "c1 int DEFAULT 1234, " + + "c2 bit, " + + "c3 char DEFAULT NULL, " + + "c4 date, " + + "c5 datetime2, " + + "c6 float, " + + "c7 nchar, " + + "c8 varchar(20), " + + "c9 varchar(max)" + + ")"; + stmt.execute(sql1); stmt.close(); } + + @AfterAll + public static void terminateVariation() throws SQLException { + connection = DriverManager.getConnection(connectionString); + + SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); + Utils.dropTableIfExists(tableName, stmt); + + if (null != pstmt) { + pstmt.close(); + } + if (null != stmt) { + stmt.close(); + } + if (null != connection) { + connection.close(); + } + } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java index 930b122b5..7a18ae0fe 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java @@ -26,6 +26,7 @@ import org.junit.runner.RunWith; import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; import com.microsoft.sqlserver.jdbc.TestResource; @@ -320,7 +321,7 @@ public void batchWithLargeStringTest() throws SQLException { @Test public void batchWithLargeStringTestUseBulkCopyAPI() throws SQLException { Statement stmt = con.createStatement(); - PreparedStatement pstmt = null; + SQLServerPreparedStatement pstmt = null; ResultSet rs = null; String testTable = "TEST_TABLE_BULK_COPY"; Utils.dropTableIfExists(testTable, stmt); @@ -357,7 +358,8 @@ public void batchWithLargeStringTestUseBulkCopyAPI() throws SQLException { Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); f1.setAccessible(true); f1.set(con, true); - pstmt = con.prepareStatement("insert into " + testTable + " values (?,?)"); + pstmt = (SQLServerPreparedStatement) con.prepareStatement("insert into " + testTable + " values (?,?)"); + pstmt.setUseBulkCopyForBatchInsert(true); // 0,a pstmt.setInt(1, 0); pstmt.setNString(2, values[0]); @@ -395,7 +397,7 @@ public void batchWithLargeStringTestUseBulkCopyAPI() throws SQLException { Map selectedValues = new LinkedHashMap<>(); int id = 0; try { - pstmt = con.prepareStatement("select * from " + testTable + ";"); + pstmt = (SQLServerPreparedStatement) con.prepareStatement("select * from " + testTable + ";"); try { rs = pstmt.executeQuery(); int i = 0; From dcd63d32653fdc5e9826c4649b313dc2bfbdd32b Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Fri, 8 Jun 2018 16:26:22 -0700 Subject: [PATCH 52/84] inherit the connection property in statement + fix issue with null / empty string being passed in as values --- .../jdbc/SQLServerBulkBatchInsertRecord.java | 11 ++-- .../jdbc/SQLServerPreparedStatement.java | 10 ++-- .../BatchExecutionWithBulkCopyTest.java | 54 +++++++++++++++++++ 3 files changed, 67 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java index a355d45e5..86b920bdd 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java @@ -260,7 +260,7 @@ public Object[] getRowData() throws SQLServerException { rowData = batchParam.get(batchParamIndex)[valueIndex++].getSetterValue(); } else if (valueData.equalsIgnoreCase("null")) { - rowData = ""; + rowData = null; } // if the user has provided a hardcoded value for this column, rowData is simply set to the hardcoded value. else { @@ -280,7 +280,7 @@ else if (valueData.equalsIgnoreCase("null")) { rowData = batchParam.get(batchParamIndex)[valueIndex++].getSetterValue(); } else if (valueData.equalsIgnoreCase("null")) { - rowData = ""; + rowData = null; } else { rowData = removeSingleQuote(valueData); @@ -288,14 +288,17 @@ else if (valueData.equalsIgnoreCase("null")) { columnListIndex++; } else { - rowData = ""; + rowData = null; } } try { - if (0 == rowData.toString().length()) { + if (null == rowData) { data[index] = null; continue; + } else if (0 == rowData.toString().length()) { + data[index] = ""; + continue; } data[index] = convertValue(pair.getValue(), rowData); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 46a43f8a7..4182efca5 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -232,6 +232,7 @@ String getClassNameInternal() { bReturnValueSyntax = parsedSQL.bReturnValueSyntax; userSQL = parsedSQL.processedSQL; initParams(parsedSQL.parameterCount); + useBulkCopyForBatchInsert = conn.getUseBulkCopyForBatchInsert(); } /** @@ -2479,7 +2480,7 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL localUserSQL = userSQL; try { - if (isInsert(localUserSQL) && connection.isAzureDW() && (this.useBulkCopyForBatchInsert || connection.getUseBulkCopyForBatchInsert())) { + if (isInsert(localUserSQL) && connection.isAzureDW() && (this.useBulkCopyForBatchInsert)) { if (batchParamValues == null) { updateCounts = new int[0]; loggerExternal.exiting(getClassNameLogging(), "executeBatch", updateCounts); @@ -2696,7 +2697,8 @@ private String parseUserSQLForTableNameDW(boolean hasInsertBeenFound, boolean ha if (localUserSQL.charAt(tempint + 1) == '.') { String tempstr = localUserSQL.substring(1, tempint); localUserSQL = localUserSQL.substring(tempint + 2); - return tempstr + "." + parseUserSQLForTableNameDW(hasInsertBeenFound, hasIntoBeenFound); + // assume that "INSERT" and "INTO" has been found, since we're at the table part already. + return tempstr + "." + parseUserSQLForTableNameDW(true, true); } else { // return tablename String tempstr = localUserSQL.substring(1, tempint); @@ -2721,7 +2723,7 @@ private String parseUserSQLForTableNameDW(boolean hasInsertBeenFound, boolean ha if (localUserSQL.charAt(tempint + 1) == '.') { String tempstr = localUserSQL.substring(1, tempint); localUserSQL = localUserSQL.substring(tempint + 2); - return tempstr + "." + parseUserSQLForTableNameDW(hasInsertBeenFound, hasIntoBeenFound); + return tempstr + "." + parseUserSQLForTableNameDW(true, true); } else { // return tablename String tempstr = localUserSQL.substring(1, tempint); @@ -2737,7 +2739,7 @@ private String parseUserSQLForTableNameDW(boolean hasInsertBeenFound, boolean ha || localUserSQL.charAt(0) == '(') { if (localUserSQL.charAt(0) == '.') { localUserSQL = localUserSQL.substring(1); - return sb.toString() + "." + parseUserSQLForTableNameDW(hasInsertBeenFound, hasIntoBeenFound); + return sb.toString() + "." + parseUserSQLForTableNameDW(true, true); } else { return sb.toString(); } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java index 5335f4401..377fb39af 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java @@ -15,6 +15,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; +import java.sql.Types; import java.util.ArrayList; import org.junit.jupiter.api.AfterAll; @@ -28,6 +29,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerConnection; import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBConnection; import com.microsoft.sqlserver.testframework.Utils; @@ -262,6 +264,58 @@ public void testMixColumns() throws Exception { } } } + + @Test + public void testNullOrEmptyColumns() throws Exception { + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); + f1.setAccessible(true); + f1.set(connection, true); + + String valid = "INSERT INTO " + tableName + " (c1, c2, c3, c4, c5, c6, c7) values " + + "(" + + "?, " + + "?, " + + "?, " + + "?, " + + "?, " + + "?, " + + "?, " + + ")"; + + SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); + SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); + + pstmt.setInt(1, 1234); + pstmt.setBoolean(2, false); + pstmt.setString(3, null); + pstmt.setDate(4, null); + pstmt.setDateTime(5, null); + pstmt.setFloat(6, (float) 123.45); + pstmt.setString(7, ""); + pstmt.addBatch(); + + pstmt.executeBatch(); + + ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName); + + Object[] expected = new Object[9]; + + expected[0] = 1234; + expected[1] = false; + expected[2] = null; + expected[3] = null; + expected[4] = null; + expected[5] = 123.45; + expected[6] = " "; + + rs.next(); + for (int i=0; i < expected.length; i++) { + if (null != rs.getObject(i + 1)) { + assertEquals(rs.getObject(i + 1), expected[i]); + } + } + } + @BeforeEach public void testSetup() throws TestAbortedException, Exception { connection = DriverManager.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); From 588743960cebca4be6176808a946a374eb64e255 Mon Sep 17 00:00:00 2001 From: ulvii Date: Fri, 8 Jun 2018 16:44:21 -0700 Subject: [PATCH 53/84] Request Boundary methods - beginRequest()/endRequest() implementation (#708) * Add | Request Boundary Methods - beginRequest()/endRequest() implementation * Fix | Remove unused import from AbstractTest * Fix | Applying review comments * Fix | Moving RequestBoundaryMethodsTest.java to connection package --- .../jdbc/ISQLServerConnection43.java | 13 +- .../sqlserver/jdbc/SQLServerConnection.java | 132 +++++- .../sqlserver/jdbc/SQLServerConnection43.java | 55 ++- .../sqlserver/jdbc/SQLServerStatement.java | 3 +- .../RequestBoundaryMethodsTest.java | 409 ++++++++++++++++++ .../sqlserver/testframework/AbstractTest.java | 1 - .../sqlserver/testframework/Utils.java | 15 +- 7 files changed, 606 insertions(+), 22 deletions(-) create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java index 1689cde87..b8230f5f9 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java @@ -1,19 +1,24 @@ package com.microsoft.sqlserver.jdbc; +import java.sql.SQLException; import java.sql.ShardingKey; public interface ISQLServerConnection43 extends ISQLServerConnection { + public void beginRequest() throws SQLException; + + public void endRequest() throws SQLException; + public void setShardingKey(ShardingKey shardingKey) throws SQLServerException; - + public void setShardingKey(ShardingKey shardingKey, ShardingKey superShardingKey) throws SQLServerException; - + public boolean setShardingKeyIfValid(ShardingKey shardingKey, int timeout) throws SQLServerException; - + public boolean setShardingKeyIfValid(ShardingKey shardingKey, ShardingKey superShardingKey, int timeout) throws SQLServerException; - + } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index ab7a758aa..918d58ee4 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -39,6 +39,7 @@ import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; @@ -3218,6 +3219,9 @@ public Statement createStatement(int resultSetType, checkClosed(); Statement st = new SQLServerStatement(this, resultSetType, resultSetConcurrency, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); + if (requestStarted) { + addOpenStatement(st); + } loggerExternal.exiting(getClassNameLogging(), "createStatement", st); return st; } @@ -3241,7 +3245,9 @@ public PreparedStatement prepareStatement(String sql, st = new SQLServerPreparedStatement(this, sql, resultSetType, resultSetConcurrency, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); } - + if (requestStarted) { + addOpenStatement(st); + } loggerExternal.exiting(getClassNameLogging(), "prepareStatement", st); return st; } @@ -3264,7 +3270,9 @@ private PreparedStatement prepareStatement(String sql, else { st = new SQLServerPreparedStatement(this, sql, resultSetType, resultSetConcurrency, stmtColEncSetting); } - + if (requestStarted) { + addOpenStatement(st); + } loggerExternal.exiting(getClassNameLogging(), "prepareStatement", st); return st; } @@ -3288,7 +3296,9 @@ public CallableStatement prepareCall(String sql, st = new SQLServerCallableStatement(this, sql, resultSetType, resultSetConcurrency, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); } - + if (requestStarted) { + addOpenStatement(st); + } loggerExternal.exiting(getClassNameLogging(), "prepareCall", st); return st; } @@ -4646,6 +4656,9 @@ public Statement createStatement(int nType, checkValidHoldability(resultSetHoldability); checkMatchesCurrentHoldability(resultSetHoldability); Statement st = new SQLServerStatement(this, nType, nConcur, stmtColEncSetting); + if (requestStarted) { + addOpenStatement(st); + } loggerExternal.exiting(getClassNameLogging(), "createStatement", st); return st; } @@ -4708,7 +4721,9 @@ public PreparedStatement prepareStatement(java.lang.String sql, else { st = new SQLServerPreparedStatement(this, sql, nType, nConcur, stmtColEncSetting); } - + if (requestStarted) { + addOpenStatement(st); + } loggerExternal.exiting(getClassNameLogging(), "prepareStatement", st); return st; } @@ -4744,7 +4759,9 @@ public CallableStatement prepareCall(String sql, else { st = new SQLServerCallableStatement(this, sql, nType, nConcur, stmtColEncSetiing); } - + if (requestStarted) { + addOpenStatement(st); + } loggerExternal.exiting(getClassNameLogging(), "prepareCall", st); return st; } @@ -5296,14 +5313,87 @@ public T unwrap(Class iface) throws SQLException { return t; } - public void beginRequest() throws SQLFeatureNotSupportedException { - DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLFeatureNotSupportedException("beginRequest not implemented"); + private boolean requestStarted = false; + private boolean originalDatabaseAutoCommitMode; + private int originalTransactionIsolationLevel; + private int originalNetworkTimeout; + private int originalHoldability; + private boolean originalSendTimeAsDatetime; + private int originalStatementPoolingCacheSize; + private boolean originalDisableStatementPooling; + private int originalServerPreparedStatementDiscardThreshold; + private Boolean originalEnablePrepareOnFirstPreparedStatementCall; + private String originalSCatalog; + private volatile SQLWarning originalSqlWarnings; + private List openStatements; + + protected void beginRequestInternal() throws SQLException { + synchronized (this) { + if (!requestStarted) { + originalDatabaseAutoCommitMode = databaseAutoCommitMode; + originalTransactionIsolationLevel = transactionIsolationLevel; + originalNetworkTimeout = getNetworkTimeout(); + originalHoldability = holdability; + originalSendTimeAsDatetime = sendTimeAsDatetime; + originalStatementPoolingCacheSize = statementPoolingCacheSize; + originalDisableStatementPooling = disableStatementPooling; + originalServerPreparedStatementDiscardThreshold = getServerPreparedStatementDiscardThreshold(); + originalEnablePrepareOnFirstPreparedStatementCall = getEnablePrepareOnFirstPreparedStatementCall(); + originalSCatalog = sCatalog; + originalSqlWarnings = sqlWarnings; + openStatements = new LinkedList(); + requestStarted = true; + } + } } - public void endRequest() throws SQLFeatureNotSupportedException { - DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLFeatureNotSupportedException("endRequest not implemented"); + protected void endRequestInternal() throws SQLException { + synchronized (this) { + if (requestStarted) { + if (!databaseAutoCommitMode) { + rollback(); + } + if (databaseAutoCommitMode != originalDatabaseAutoCommitMode) { + setAutoCommit(originalDatabaseAutoCommitMode); + } + if (transactionIsolationLevel != originalTransactionIsolationLevel) { + setTransactionIsolation(originalTransactionIsolationLevel); + } + if (getNetworkTimeout() != originalNetworkTimeout) { + setNetworkTimeout(null, originalNetworkTimeout); + } + if (holdability != originalHoldability) { + setHoldability(originalHoldability); + } + if (sendTimeAsDatetime != originalSendTimeAsDatetime) { + setSendTimeAsDatetime(originalSendTimeAsDatetime); + } + if (statementPoolingCacheSize != originalStatementPoolingCacheSize) { + setStatementPoolingCacheSize(originalStatementPoolingCacheSize); + } + if (disableStatementPooling != originalDisableStatementPooling) { + setDisableStatementPooling(originalDisableStatementPooling); + } + if (getServerPreparedStatementDiscardThreshold() != originalServerPreparedStatementDiscardThreshold) { + setServerPreparedStatementDiscardThreshold(originalServerPreparedStatementDiscardThreshold); + } + if (getEnablePrepareOnFirstPreparedStatementCall() != originalEnablePrepareOnFirstPreparedStatementCall) { + setEnablePrepareOnFirstPreparedStatementCall(originalEnablePrepareOnFirstPreparedStatementCall); + } + if (!sCatalog.equals(originalSCatalog)) { + setCatalog(originalSCatalog); + } + sqlWarnings = originalSqlWarnings; + if (null != openStatements) { + while (!openStatements.isEmpty()) { + try (Statement st = openStatements.get(0)) { + } + } + openStatements.clear(); + } + requestStarted = false; + } + } } /** @@ -5879,6 +5969,26 @@ public void onEviction(Sha1HashKey key, PreparedStatementHandle handle) { } } } + + /** + * @param st + * Statement to add to openStatements + */ + final synchronized void addOpenStatement(Statement st) { + if (null != openStatements) { + openStatements.add(st); + } + } + + /** + * @param st + * Statement to remove from openStatements + */ + final synchronized void removeOpenStatement(Statement st) { + if (null != openStatements) { + openStatements.remove(st); + } + } } // Helper class for security manager functions used by SQLServerConnection class. diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java index 217bcfc72..bf1dcd5f2 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java @@ -1,5 +1,6 @@ package com.microsoft.sqlserver.jdbc; +import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.ShardingKey; @@ -8,7 +9,51 @@ public class SQLServerConnection43 extends SQLServerConnection implements ISQLSe SQLServerConnection43(String parentInfo) throws SQLServerException { super(parentInfo); } - + + /** + * Hints to the driver that a request, an independent unit of work, is beginning on this connection. It backs up the values of the connection + * properties that are modifiable through public methods. Each request is independent of all other requests with regard to state local to the + * connection either on the client or the server. Work done between {@code beginRequest}, {@code endRequest} pairs does not depend on any other + * work done on the connection either as part of another request or outside of any request. A request may include multiple transactions. There may + * be dependencies on committed database state as that is not local to the connection. {@code beginRequest} marks the beginning of the work unit. + *

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

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

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

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

+ * + * @throws SQLException + * if an error occurs + * @see beginRequest + */ + public void endRequest() throws SQLException { + endRequestInternal(); + } + public void setShardingKey(ShardingKey shardingKey) throws SQLServerException { DriverJDBCVersion.checkSupportsJDBC43(); throw new SQLServerException("setShardingKey not implemented", new SQLFeatureNotSupportedException("setShardingKey not implemented")); @@ -17,20 +62,22 @@ public void setShardingKey(ShardingKey shardingKey) throws SQLServerException { public void setShardingKey(ShardingKey shardingKey, ShardingKey superShardingKey) throws SQLServerException { DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLServerException("setShardingKey not implemented", new SQLFeatureNotSupportedException("setShardingKey not implemented")) ; + throw new SQLServerException("setShardingKey not implemented", new SQLFeatureNotSupportedException("setShardingKey not implemented")); } public boolean setShardingKeyIfValid(ShardingKey shardingKey, int timeout) throws SQLServerException { DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLServerException("setShardingKeyIfValid not implemented", new SQLFeatureNotSupportedException("setShardingKeyIfValid not implemented")); + throw new SQLServerException("setShardingKeyIfValid not implemented", + new SQLFeatureNotSupportedException("setShardingKeyIfValid not implemented")); } public boolean setShardingKeyIfValid(ShardingKey shardingKey, ShardingKey superShardingKey, int timeout) throws SQLServerException { DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLServerException("setShardingKeyIfValid not implemented", new SQLFeatureNotSupportedException("setShardingKeyIfValid not implemented")); + throw new SQLServerException("setShardingKeyIfValid not implemented", + new SQLFeatureNotSupportedException("setShardingKeyIfValid not implemented")); } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index 441d8b5b0..162120ae9 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -631,13 +631,14 @@ void closeInternal() { // Regardless what happens when cleaning up, // the statement is considered closed. assert !bIsClosed; - discardLastExecutionResults(); bIsClosed = true; autoGeneratedKeys = null; sqlWarnings = null; inOutParam = null; + + connection.removeOpenStatement(this); } public void close() throws SQLServerException { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java new file mode 100644 index 000000000..e646f63ed --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java @@ -0,0 +1,409 @@ +package com.microsoft.sqlserver.jdbc.connection; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.concurrent.CountDownLatch; + +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; +import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.PrepUtil; +import com.microsoft.sqlserver.testframework.Utils; +import com.microsoft.sqlserver.testframework.util.RandomUtil; + +/** + * A class for testing Request Boundary Methods. + */ +@RunWith(JUnitPlatform.class) +public class RequestBoundaryMethodsTest extends AbstractTest { + + /** + * Tests Request Boundary methods with SQLServerConnection properties that are modifiable through public APIs. + * + * @throws SQLException + */ + + @Test + public void testModifiableConnectionProperties() throws SQLException { + // List of SQLServerConnection fields that can be modified through public APIs. + boolean autoCommitMode1 = true; + int transactionIsolationLevel1 = SQLServerConnection.TRANSACTION_READ_COMMITTED; + int networkTimeout1 = 5000; + int holdability1 = ResultSet.HOLD_CURSORS_OVER_COMMIT; + boolean sendTimeAsDatetime1 = true; + int statementPoolingCacheSize1 = 0; + boolean disableStatementPooling1 = true; + int serverPreparedStatementDiscardThreshold1 = 10; + boolean enablePrepareOnFirstPreparedStatementCall1 = false; + String sCatalog1 = "master"; + + boolean autoCommitMode2 = false; + int transactionIsolationLevel2 = SQLServerConnection.TRANSACTION_SERIALIZABLE; + int networkTimeout2 = 10000; + int holdability2 = ResultSet.CLOSE_CURSORS_AT_COMMIT; + boolean sendTimeAsDatetime2 = false; + int statementPoolingCacheSize2 = 10; + boolean disableStatementPooling2 = false; + int serverPreparedStatementDiscardThreshold2 = 100; + boolean enablePrepareOnFirstPreparedStatementCall2 = true; + String sCatalog2 = RandomUtil.getIdentifier("RequestBoundaryDatabase"); + + try (SQLServerConnection con = connect()) { + if (Utils.isJDBC43OrGreater(con)) { + // Second database + con.createStatement().executeUpdate("CREATE DATABASE [" + sCatalog2 + "]"); + + // First set of values. + setConnectionFields(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, + statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, + enablePrepareOnFirstPreparedStatementCall1, sCatalog1); + con.beginRequest(); + // Call setters with the second set of values inside beginRequest()/endRequest() block. + setConnectionFields(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, + statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, + enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + con.endRequest(); + // Test if endRequest() resets the SQLServerConnection properties back to the first set of values. + compareValuesAgainstConnection(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, + statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, + enablePrepareOnFirstPreparedStatementCall1, sCatalog1); + + // Multiple calls to beginRequest() without an intervening call to endRequest() are no-op. + setConnectionFields(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, + statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, + enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + con.beginRequest(); + setConnectionFields(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, + statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, + enablePrepareOnFirstPreparedStatementCall1, sCatalog1); + con.beginRequest(); + con.endRequest(); + // Same values as before the first beginRequest() + compareValuesAgainstConnection(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, + statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, + enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + + // A call to endRequest() without an intervening call to beginRequest() is no-op. + setConnectionFields(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, + statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, + enablePrepareOnFirstPreparedStatementCall1, sCatalog1); + setConnectionFields(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, + statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, + enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + con.endRequest(); + // No change. + compareValuesAgainstConnection(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, + statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, + enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + // drop the database + con.setCatalog("master"); + Utils.dropDatabaseIfExists(sCatalog2, con.createStatement()); + } + } + } + + /** + * Tests Request Boundary methods with warnings. + * + * @throws SQLException + */ + @Test + public void testWarnings() throws SQLException { + try (SQLServerConnection con = connect()) { + if (Utils.isJDBC43OrGreater(con)) { + con.beginRequest(); + generateWarning(con); + assertNotNull(con.getWarnings()); + con.endRequest(); + assertNull(con.getWarnings()); + + generateWarning(con); + con.endRequest(); + assertNotNull(con.getWarnings()); + + con.clearWarnings(); + con.beginRequest(); + generateWarning(con); + con.beginRequest(); + con.endRequest(); + assertNull(con.getWarnings()); + } + } + } + + /** + * Tests Request Boundary methods when there are open transactions. + * + * @throws SQLException + */ + @Test + public void testOpenTransactions() throws SQLException { + ResultSet rs = null; + String tableName = null; + + try (SQLServerConnection con = connect(); Statement stmt = con.createStatement();) { + if (Utils.isJDBC43OrGreater(con)) { + tableName = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("RequestBoundaryTable")); + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("CREATE TABLE " + tableName + " (col int)"); + con.beginRequest(); + con.setAutoCommit(false); + stmt.executeUpdate("INSERT INTO " + tableName + " values(5)"); + // endRequest() does a rollback here, the value does not get inserted into the table. + con.endRequest(); + con.commit(); + + rs = con.createStatement().executeQuery("SELECT * from " + tableName); + assertTrue(!rs.isBeforeFirst(), "Should not have returned a result set."); + Utils.dropTableIfExists(tableName, con.createStatement()); + } + } + } + + /** + * Tests Request Boundary methods with statements. + * + * @throws SQLException + */ + @SuppressWarnings("resource") + @Test + public void testStatements() throws SQLException { + Statement stmt = null; + ResultSet rs = null; + Statement stmt1 = null; + PreparedStatement ps = null; + CallableStatement cs = null; + ResultSet rs1 = null; + String tableName = null; + + try (SQLServerConnection con = connect();) { + if (Utils.isJDBC43OrGreater(con)) { + stmt1 = con.createStatement(); + con.beginRequest(); + stmt = con.createStatement(); + rs = stmt.executeQuery("SELECT 1"); + rs.next(); + assertEquals(1, rs.getInt(1)); + con.endRequest(); + + assertTrue(!stmt1.isClosed(), "Statement created outside of beginRequest()/endRequest() block should not be closed."); + assertTrue(stmt.isClosed(), "Statment created inside beginRequest()/endRequest() block should be closed after endRequest()."); + assertTrue(rs.isClosed(), "ResultSet should be closed after endRequest()."); + stmt1.close(); + + // Multiple statements inside beginRequest()/endRequest() block + con.beginRequest(); + stmt = con.createStatement(); + tableName = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("RequestBoundary")); + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("CREATE TABLE " + tableName + " (col int)"); + ps = con.prepareStatement("INSERT INTO " + tableName + " values (?)"); + ps.setInt(1, 2); + ps.executeUpdate(); + + stmt1 = con.createStatement(); + rs1 = stmt1.executeQuery("SELECT * FROM " + tableName); + rs1.next(); + assertEquals(2, rs1.getInt(1)); + Utils.dropTableIfExists(tableName, stmt); + + cs = con.prepareCall("{call sp_server_info}"); + cs.execute(); + con.endRequest(); + + assertTrue(stmt.isClosed()); + assertTrue(ps.isClosed()); + assertTrue(stmt1.isClosed()); + assertTrue(cs.isClosed()); + assertTrue(rs1.isClosed()); + } + } + finally { + if (null != stmt) { + stmt.close(); + } + if (null != stmt1) { + stmt1.close(); + } + if (null != ps) { + ps.close(); + } + if (null != cs) { + cs.close(); + } + } + } + + /** + * Tests Request Boundary methods in a multi-threaded environment. + * + * @throws SQLException + */ + @Test + public void testThreads() throws SQLException { + class Variables { + volatile SQLServerConnection con = null; + volatile Statement stmt = null; + volatile PreparedStatement pstmt = null; + } + + final Variables sharedVariables = new Variables(); + final CountDownLatch latch = new CountDownLatch(3); + try { + sharedVariables.con = connect(); + if (Utils.isJDBC43OrGreater(sharedVariables.con)) { + Thread thread1 = new Thread() { + public void run() { + try { + sharedVariables.con.setNetworkTimeout(null, 100); + sharedVariables.con.setHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT); + latch.countDown(); + } + catch (SQLException e) { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + }; + + Thread thread2 = new Thread() { + public void run() { + try { + sharedVariables.stmt = sharedVariables.con.createStatement(); + ResultSet rs = sharedVariables.stmt.executeQuery("SELECT 1"); + rs.next(); + assertEquals(1, rs.getInt(1)); + latch.countDown(); + } + catch (SQLException e) { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + }; + + Thread thread3 = new Thread() { + public void run() { + try { + sharedVariables.pstmt = sharedVariables.con.prepareStatement("SELECT 1"); + ResultSet rs = sharedVariables.pstmt.executeQuery(); + rs.next(); + assertEquals(1, rs.getInt(1)); + latch.countDown(); + } + catch (SQLException e) { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + + } + }; + + int originalNetworkTimeout = sharedVariables.con.getNetworkTimeout(); + int originalHoldability = sharedVariables.con.getHoldability(); + sharedVariables.con.beginRequest(); + thread1.start(); + thread2.start(); + thread3.start(); + latch.await(); + sharedVariables.con.endRequest(); + + assertEquals(originalNetworkTimeout, sharedVariables.con.getNetworkTimeout()); + assertEquals(originalHoldability, sharedVariables.con.getHoldability()); + assertTrue(sharedVariables.stmt.isClosed()); + assertTrue(sharedVariables.pstmt.isClosed()); + } + } + catch (InterruptedException e) { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + finally { + if (null != sharedVariables.stmt) { + sharedVariables.stmt.close(); + } + if (null != sharedVariables.pstmt) { + sharedVariables.pstmt.close(); + } + if (null != sharedVariables.con) { + sharedVariables.con.close(); + } + } + } + + private SQLServerConnection connect() throws SQLException { + SQLServerConnection connection = null; + try { + connection = PrepUtil.getConnection(getConfiguredProperty("mssql_jdbc_test_connection_properties")); + } + catch (ClassNotFoundException e) { + e.printStackTrace(); + } + return connection; + } + + private void setConnectionFields(SQLServerConnection con, + boolean autoCommitMode, + int transactionIsolationLevel, + int networkTimeout, + int holdability, + boolean sendTimeAsDatetime, + int statementPoolingCacheSize, + boolean disableStatementPooling, + int serverPreparedStatementDiscardThreshold, + boolean enablePrepareOnFirstPreparedStatementCall, + String sCatalog) throws SQLException { + con.setAutoCommit(autoCommitMode); + con.setTransactionIsolation(transactionIsolationLevel); + con.setNetworkTimeout(null, networkTimeout); + con.setHoldability(holdability); + con.setSendTimeAsDatetime(sendTimeAsDatetime); + con.setStatementPoolingCacheSize(statementPoolingCacheSize); + con.setDisableStatementPooling(disableStatementPooling); + con.setServerPreparedStatementDiscardThreshold(serverPreparedStatementDiscardThreshold); + con.setEnablePrepareOnFirstPreparedStatementCall(enablePrepareOnFirstPreparedStatementCall); + con.setCatalog(sCatalog); + } + + private void compareValuesAgainstConnection(SQLServerConnection con, + boolean autoCommitMode, + int transactionIsolationLevel, + int networkTimeout, + int holdability, + boolean sendTimeAsDatetime, + int statementPoolingCacheSize, + boolean disableStatementPooling, + int serverPreparedStatementDiscardThreshold, + boolean enablePrepareOnFirstPreparedStatementCall, + String sCatalog) throws SQLException { + final String description = " values do not match."; + assertEquals(autoCommitMode, con.getAutoCommit(), "autoCommitmode" + description); + assertEquals(transactionIsolationLevel, con.getTransactionIsolation(), "transactionIsolationLevel" + description); + assertEquals(networkTimeout, con.getNetworkTimeout(), "networkTimeout" + description); + assertEquals(holdability, con.getHoldability(), "holdability" + description); + assertEquals(sendTimeAsDatetime, con.getSendTimeAsDatetime(), "sendTimeAsDatetime" + description); + assertEquals(statementPoolingCacheSize, con.getStatementPoolingCacheSize(), "statementPoolingCacheSize" + description); + assertEquals(disableStatementPooling, con.getDisableStatementPooling(), "disableStatementPooling" + description); + assertEquals(serverPreparedStatementDiscardThreshold, con.getServerPreparedStatementDiscardThreshold(), + "serverPreparedStatementDiscardThreshold" + description); + assertEquals(enablePrepareOnFirstPreparedStatementCall, con.getEnablePrepareOnFirstPreparedStatementCall(), + "enablePrepareOnFirstPreparedStatementCall" + description); + assertEquals(sCatalog, con.getCatalog(), "sCatalog" + description); + } + + private void generateWarning(SQLServerConnection con) throws SQLException { + con.setClientInfo("name", "value"); + } +} diff --git a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java index e72ab2dc5..3df03ef53 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java @@ -181,5 +181,4 @@ else if ("file".equalsIgnoreCase(loggingHandler)) { System.err.println("Some how could not invoke logging: " + e.getMessage()); } } - } diff --git a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java index 229f7f4c6..cf761bbb1 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java @@ -14,6 +14,7 @@ import java.io.ByteArrayInputStream; import java.io.CharArrayReader; import java.net.URI; +import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; @@ -283,6 +284,11 @@ public static void dropProcedureIfExists(String procName, java.sql.Statement stm dropObjectIfExists(procName, "IsProcedure", stmt); } + public static void dropDatabaseIfExists(String databaseName, + java.sql.Statement stmt) throws SQLException { + stmt.executeUpdate("IF EXISTS(SELECT * from sys.databases WHERE name='" + databaseName + "') DROP DATABASE [" + databaseName + "]"); + } + /** * actually perform the "DROP TABLE / PROCEDURE" */ @@ -315,4 +321,11 @@ public static boolean parseByte(byte[] expectedData, return true; } -} \ No newline at end of file + public static boolean isJDBC43OrGreater(Connection connection) throws SQLException{ + return getJDBCVersion(connection) >= 4.3F; + } + + public static float getJDBCVersion(Connection connection) throws SQLException { + return Float.valueOf(connection.getMetaData().getJDBCMajorVersion() + "." + connection.getMetaData().getJDBCMinorVersion()); + } +} From 29cc48cf6a344aaf447c56790391a5b953d37030 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 12 Jun 2018 15:12:05 -0700 Subject: [PATCH 54/84] added error message in resource file and changed files accordingly --- .../microsoft/sqlserver/jdbc/Geography.java | 27 +++-- .../microsoft/sqlserver/jdbc/Geometry.java | 27 +++-- .../sqlserver/jdbc/SQLServerResource.java | 3 + .../jdbc/SQLServerSpatialDatatype.java | 102 ++++++++++++------ .../SQLServerSpatialDatatypeTest.java | 61 +++++------ 5 files changed, 137 insertions(+), 83 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Geography.java b/src/main/java/com/microsoft/sqlserver/jdbc/Geography.java index 0790a0691..a565d5084 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Geography.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Geography.java @@ -15,8 +15,9 @@ public class Geography extends SQLServerSpatialDatatype { /** * Private constructor used for creating a Geography object from WKT and srid. + * @throws SQLServerException */ - private Geography(String WellKnownText, int srid) { + private Geography(String WellKnownText, int srid) throws SQLServerException { this.wkt = WellKnownText; this.srid = srid; @@ -24,7 +25,8 @@ private Geography(String WellKnownText, int srid) { parseWKTForSerialization(this, currentWktPos, -1, false); } catch (StringIndexOutOfBoundsException e) { - throw new IllegalArgumentException("Reached unexpected end of WKT. Please make sure WKT is valid."); + String strError = SQLServerException.getErrString("R_illegalWKT"); + throw new SQLServerException(strError, null, 0, null); } serializeToWkb(false); @@ -33,8 +35,9 @@ private Geography(String WellKnownText, int srid) { /** * Private constructor used for creating a Geography object from WKB. + * @throws SQLServerException */ - private Geography(byte[] wkb) { + private Geography(byte[] wkb) throws SQLServerException { this.wkb = wkb; buffer = ByteBuffer.wrap(wkb); buffer.order(ByteOrder.LITTLE_ENDIAN); @@ -62,8 +65,9 @@ public Geography() { * @param wkt WKT * @param srid SRID * @return Geography instance + * @throws SQLServerException */ - public static Geography STGeomFromText(String wkt, int srid) { + public static Geography STGeomFromText(String wkt, int srid) throws SQLServerException { return new Geography(wkt, srid); } @@ -73,8 +77,9 @@ public static Geography STGeomFromText(String wkt, int srid) { * * @param wkb WKB * @return Geography instance + * @throws SQLServerException */ - public static Geography STGeomFromWKB(byte[] wkb) { + public static Geography STGeomFromWKB(byte[] wkb) throws SQLServerException { return new Geography(wkb); } @@ -83,8 +88,9 @@ public static Geography STGeomFromWKB(byte[] wkb) { * * @param wkb WKB * @return Geography instance + * @throws SQLServerException */ - public static Geography deserialize(byte[] wkb) { + public static Geography deserialize(byte[] wkb) throws SQLServerException { return new Geography(wkb); } @@ -94,8 +100,9 @@ public static Geography deserialize(byte[] wkb) { * * @param wkt WKt * @return Geography instance + * @throws SQLServerException */ - public static Geography parse(String wkt) { + public static Geography parse(String wkt) throws SQLServerException { return new Geography(wkt, 4326); } @@ -106,8 +113,9 @@ public static Geography parse(String wkt) { * @param y y coordinate * @param srid SRID * @return Geography instance + * @throws SQLServerException */ - public static Geography point(double x, double y, int srid) { + public static Geography point(double x, double y, int srid) throws SQLServerException { return new Geography("POINT (" + x + " " + y + ")", srid); } @@ -116,8 +124,9 @@ public static Geography point(double x, double y, int srid) { * Geography instance. This text will not contain any Z (elevation) or M (measure) values carried by the instance. * * @return the WKT representation without the Z and M values. + * @throws SQLServerException */ - public String STAsText() { + public String STAsText() throws SQLServerException { if (null == wktNoZM) { buffer = ByteBuffer.wrap(wkb); buffer.order(ByteOrder.LITTLE_ENDIAN); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Geometry.java b/src/main/java/com/microsoft/sqlserver/jdbc/Geometry.java index 90f4427a5..196f6a0ad 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Geometry.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Geometry.java @@ -15,8 +15,9 @@ public class Geometry extends SQLServerSpatialDatatype { /** * Private constructor used for creating a Geometry object from WKT and srid. + * @throws SQLServerException */ - private Geometry(String WellKnownText, int srid) { + private Geometry(String WellKnownText, int srid) throws SQLServerException { this.wkt = WellKnownText; this.srid = srid; @@ -24,7 +25,8 @@ private Geometry(String WellKnownText, int srid) { parseWKTForSerialization(this, currentWktPos, -1, false); } catch (StringIndexOutOfBoundsException e) { - throw new IllegalArgumentException("Reached unexpected end of WKT. Please make sure WKT is valid."); + String strError = SQLServerException.getErrString("R_illegalWKT"); + throw new SQLServerException(strError, null, 0, null); } serializeToWkb(false); @@ -33,8 +35,9 @@ private Geometry(String WellKnownText, int srid) { /** * Private constructor used for creating a Geometry object from WKB. + * @throws SQLServerException */ - private Geometry(byte[] wkb) { + private Geometry(byte[] wkb) throws SQLServerException { this.wkb = wkb; buffer = ByteBuffer.wrap(wkb); buffer.order(ByteOrder.LITTLE_ENDIAN); @@ -62,8 +65,9 @@ public Geometry() { * @param wkt WKT * @param srid SRID * @return Geometry instance + * @throws SQLServerException */ - public static Geometry STGeomFromText(String wkt, int srid) { + public static Geometry STGeomFromText(String wkt, int srid) throws SQLServerException { return new Geometry(wkt, srid); } @@ -73,8 +77,9 @@ public static Geometry STGeomFromText(String wkt, int srid) { * * @param wkb WKB * @return Geometry instance + * @throws SQLServerException */ - public static Geometry STGeomFromWKB(byte[] wkb) { + public static Geometry STGeomFromWKB(byte[] wkb) throws SQLServerException { return new Geometry(wkb); } @@ -83,8 +88,9 @@ public static Geometry STGeomFromWKB(byte[] wkb) { * * @param wkb WKB * @return Geometry instance + * @throws SQLServerException */ - public static Geometry deserialize(byte[] wkb) { + public static Geometry deserialize(byte[] wkb) throws SQLServerException { return new Geometry(wkb); } @@ -94,8 +100,9 @@ public static Geometry deserialize(byte[] wkb) { * * @param wkt WKT * @return Geometry instance + * @throws SQLServerException */ - public static Geometry parse(String wkt) { + public static Geometry parse(String wkt) throws SQLServerException { return new Geometry(wkt, 0); } @@ -106,8 +113,9 @@ public static Geometry parse(String wkt) { * @param y y coordinate * @param srid SRID * @return Geometry instance + * @throws SQLServerException */ - public static Geometry point(double x, double y, int srid) { + public static Geometry point(double x, double y, int srid) throws SQLServerException { return new Geometry("POINT (" + x + " " + y + ")", srid); } @@ -116,8 +124,9 @@ public static Geometry point(double x, double y, int srid) { * Geometry instance. This text will not contain any Z (elevation) or M (measure) values carried by the instance. * * @return the WKT representation without the Z and M values. + * @throws SQLServerException */ - public String STAsText() { + public String STAsText() throws SQLServerException { if (null == wktNoZM) { buffer = ByteBuffer.wrap(wkb); buffer.order(ByteOrder.LITTLE_ENDIAN); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 884be5c93..3cea768e6 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -393,5 +393,8 @@ protected Object[][] getContents() { {"R_invalidSSLProtocol", "SSL Protocol {0} label is not valid. Only TLS, TLSv1, TLSv1.1, and TLSv1.2 are supported."}, {"R_cancelQueryTimeoutPropertyDescription", "The number of seconds to wait to cancel sending a query timeout."}, {"R_invalidCancelQueryTimeout", "The cancel timeout value {0} is not valid."}, + {"R_illegalWKT", "Illegal Well-Known text. Please make sure Well-Known text is valid."}, + {"R_illegalTypeForGeometry", "{0} is not supported for Geometry."}, + {"R_illegalWKTposition", "Illegal character in Well-Known text at position {0}."}, }; } \ No newline at end of file diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSpatialDatatype.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSpatialDatatype.java index a5256aa10..3fe57e8dd 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSpatialDatatype.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSpatialDatatype.java @@ -10,6 +10,7 @@ import java.math.BigDecimal; import java.nio.ByteBuffer; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -107,13 +108,15 @@ abstract class SQLServerSpatialDatatype { * @param figureIndexEnd upper bound for reading figures * @param segmentIndexEnd upper bound for reading segments * @param shapeIndexEnd upper bound for reading shapes + * @throws SQLServerException */ protected void constructWKT(SQLServerSpatialDatatype sd, InternalSpatialDatatype isd, int pointIndexEnd, int figureIndexEnd, - int segmentIndexEnd, int shapeIndexEnd) { + int segmentIndexEnd, int shapeIndexEnd) throws SQLServerException { if (null == points || numberOfPoints == 0) { if (isd.getTypeCode() == 11) { // FULLGLOBE if (sd instanceof Geometry) { - throw new IllegalArgumentException("Fullglobe is not supported for Geometry."); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalTypeForGeometry")); + throw new SQLServerException(form.format(new Object[]{"Fullglobe"}), null, 0, null); } else { appendToWKTBuffers("FULLGLOBE"); return; @@ -163,7 +166,8 @@ protected void constructWKT(SQLServerSpatialDatatype sd, InternalSpatialDatatype constructCurvepolygonWKT(currentFigureIndex, figureIndexEnd, currentSegmentIndex, segmentIndexEnd); break; default: - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } appendToWKTBuffers(")"); @@ -176,8 +180,9 @@ protected void constructWKT(SQLServerSpatialDatatype sd, InternalSpatialDatatype * @param startPos The index to start from from the WKT. * @param parentShapeIndex The index of the parent's Shape in the shapes array. Used to determine this shape's parent. * @param isGeoCollection flag to indicate if this is part of a GeometryCollection. + * @throws SQLServerException */ - protected void parseWKTForSerialization(SQLServerSpatialDatatype sd, int startPos, int parentShapeIndex, boolean isGeoCollection) { + protected void parseWKTForSerialization(SQLServerSpatialDatatype sd, int startPos, int parentShapeIndex, boolean isGeoCollection) throws SQLServerException { //after every iteration of this while loop, the currentWktPosition will be set to the //end of the geometry/geography shape, except for the very first iteration of it. //This means that there has to be comma (that separates the previous shape with the next shape), @@ -199,7 +204,8 @@ protected void parseWKTForSerialization(SQLServerSpatialDatatype sd, int startPo isd = InternalSpatialDatatype.valueOf(nextToken); } catch (Exception e) { - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } byte fa = 0; @@ -211,11 +217,13 @@ protected void parseWKTForSerialization(SQLServerSpatialDatatype sd, int startPo // check for FULLGLOBE before reading the first open bracket, since FULLGLOBE doesn't have one. if (nextToken.equals("FULLGLOBE")) { if (sd instanceof Geometry) { - throw new IllegalArgumentException("Fullglobe is not supported for Geometry."); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalTypeForGeometry")); + throw new SQLServerException(form.format(new Object[]{"Fullglobe"}), null, 0, null); } if (startPos != 0) { - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } shapeList.add(new Shape(parentShapeIndex, -1, isd.getTypeCode())); @@ -293,7 +301,8 @@ protected void parseWKTForSerialization(SQLServerSpatialDatatype sd, int startPo break; default: - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } readCloseBracket(); } @@ -668,8 +677,9 @@ protected void constructSegmentWKT(int currentSegment, byte segment, int pointEn * The starting point for constructing a GeometryCollection type in WKT form. * * @param shapeEndIndex . + * @throws SQLServerException */ - protected void constructGeometryCollectionWKT(int shapeEndIndex) { + protected void constructGeometryCollectionWKT(int shapeEndIndex) throws SQLServerException { currentShapeIndex++; constructGeometryCollectionWKThelper(shapeEndIndex); } @@ -677,8 +687,9 @@ protected void constructGeometryCollectionWKT(int shapeEndIndex) { /** * Reads Point WKT and adds it to the list of points. * This method will read up until and including the comma that may come at the end of the Point WKT. + * @throws SQLServerException */ - protected void readPointWkt() { + protected void readPointWkt() throws SQLServerException { int numOfCoordinates = 0; double sign; double coords[] = new double[4]; @@ -708,7 +719,8 @@ protected void readPointWkt() { coords[numOfCoordinates] = sign * new BigDecimal(wkt.substring(startPos, currentWktPos)).doubleValue(); } catch (Exception e) { //modify to conversion exception - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } numOfCoordinates++; @@ -719,14 +731,16 @@ protected void readPointWkt() { // character has to be either a , or ), or the WKT is invalid. if (numOfCoordinates == 4) { if (wkt.charAt(currentWktPos) != ',' && wkt.charAt(currentWktPos) != ')') { - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } } if (wkt.charAt(currentWktPos) == ',') { // need at least 2 coordinates if (numOfCoordinates == 1) { - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } currentWktPos++; skipWhiteSpaces(); @@ -747,8 +761,9 @@ protected void readPointWkt() { /** * Reads a series of Point types. + * @throws SQLServerException */ - protected void readLineWkt() { + protected void readLineWkt() throws SQLServerException { while (currentWktPos < wkt.length() && wkt.charAt(currentWktPos) != ')') { readPointWkt(); } @@ -759,8 +774,9 @@ protected void readLineWkt() { * * @param parentShapeIndex shape index of the parent shape that called this method * @param nextToken next string token + * @throws SQLServerException */ - protected void readShapeWkt(int parentShapeIndex, String nextToken) { + protected void readShapeWkt(int parentShapeIndex, String nextToken) throws SQLServerException { byte fa = FA_POINT; while (currentWktPos < wkt.length() && wkt.charAt(currentWktPos) != ')') { @@ -803,15 +819,17 @@ protected void readShapeWkt(int parentShapeIndex, String nextToken) { } else if (wkt.charAt(currentWktPos) == ')') { // about to exit while loop continue; } else { // unexpected input - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } } } /** * Reads a CurvePolygon WKT + * @throws SQLServerException */ - protected void readCurvePolygon() { + protected void readCurvePolygon() throws SQLServerException { while (currentWktPos < wkt.length() && wkt.charAt(currentWktPos) != ')') { String nextPotentialToken = getNextStringToken().toUpperCase(Locale.US); if (nextPotentialToken.equals("CIRCULARSTRING")) { @@ -830,7 +848,8 @@ protected void readCurvePolygon() { readLineWkt(); readCloseBracket(); } else { - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } if (wkt.charAt(currentWktPos) == ',') { // more polygons to follow @@ -838,7 +857,8 @@ protected void readCurvePolygon() { } else if (wkt.charAt(currentWktPos) == ')') { // about to exit while loop continue; } else { // unexpected input - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } } } @@ -848,8 +868,9 @@ protected void readCurvePolygon() { * * @param thisShapeIndex shape index of current shape * @param nextToken next string token + * @throws SQLServerException */ - protected void readMultiPolygonWkt(int thisShapeIndex, String nextToken) { + protected void readMultiPolygonWkt(int thisShapeIndex, String nextToken) throws SQLServerException { while (currentWktPos < wkt.length() && wkt.charAt(currentWktPos) != ')') { if (checkEmptyKeyword(thisShapeIndex, InternalSpatialDatatype.valueOf(nextToken), true)) { continue; @@ -864,7 +885,8 @@ protected void readMultiPolygonWkt(int thisShapeIndex, String nextToken) { } else if (wkt.charAt(currentWktPos) == ')') { // about to exit while loop continue; } else { // unexpected input - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } } } @@ -874,8 +896,9 @@ protected void readMultiPolygonWkt(int thisShapeIndex, String nextToken) { * * @param segmentType segment type * @param isFirstIteration flag that indicates if this is the first iteration from the loop outside + * @throws SQLServerException */ - protected void readSegmentWkt(int segmentType, boolean isFirstIteration) { + protected void readSegmentWkt(int segmentType, boolean isFirstIteration) throws SQLServerException { segmentList.add(new Segment((byte) segmentType)); int segmentLength = segmentType; @@ -908,8 +931,9 @@ protected void readSegmentWkt(int segmentType, boolean isFirstIteration) { * Reads a CompoundCurve WKT * * @param isFirstIteration flag that indicates if this is the first iteration from the loop outside + * @throws SQLServerException */ - protected void readCompoundCurveWkt(boolean isFirstIteration) { + protected void readCompoundCurveWkt(boolean isFirstIteration) throws SQLServerException { while (currentWktPos < wkt.length() && wkt.charAt(currentWktPos) != ')') { String nextPotentialToken = getNextStringToken().toUpperCase(Locale.US); if (nextPotentialToken.equals("CIRCULARSTRING")) { @@ -921,7 +945,8 @@ protected void readCompoundCurveWkt(boolean isFirstIteration) { readSegmentWkt(SEGMENT_FIRST_LINE, isFirstIteration); readCloseBracket(); } else { - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } isFirstIteration = false; @@ -931,7 +956,8 @@ protected void readCompoundCurveWkt(boolean isFirstIteration) { } else if (wkt.charAt(currentWktPos) == ')') { // about to exit while loop continue; } else { // unexpected input - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } } } @@ -1029,23 +1055,25 @@ protected void populateStructures() { numberOfSegments = segmentList.size(); } - protected void readOpenBracket() { + protected void readOpenBracket() throws SQLServerException { skipWhiteSpaces(); if (wkt.charAt(currentWktPos) == '(') { currentWktPos++; skipWhiteSpaces(); } else { - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } } - protected void readCloseBracket() { + protected void readCloseBracket() throws SQLServerException { skipWhiteSpaces(); if (wkt.charAt(currentWktPos) == ')') { currentWktPos++; skipWhiteSpaces(); } else { - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } } @@ -1221,7 +1249,7 @@ protected void determineInternalType() { } } - protected boolean checkEmptyKeyword(int parentShapeIndex, InternalSpatialDatatype isd, boolean isInsideAnotherShape) { + protected boolean checkEmptyKeyword(int parentShapeIndex, InternalSpatialDatatype isd, boolean isInsideAnotherShape) throws SQLServerException { String potentialEmptyKeyword = getNextStringToken().toUpperCase(Locale.US); if (potentialEmptyKeyword.equals("EMPTY")) { @@ -1238,7 +1266,8 @@ protected boolean checkEmptyKeyword(int parentShapeIndex, InternalSpatialDatatyp } else if (parentTypeCode == 7) { // GeometryCollection typeCode = InternalSpatialDatatype.GEOMETRYCOLLECTION.getTypeCode(); } else { - throw new IllegalArgumentException("Illegal parentTypeCode."); + String strError = SQLServerException.getErrString("R_illegalWKT"); + throw new SQLServerException(strError, null, 0, null); } } else { typeCode = isd.getTypeCode(); @@ -1254,7 +1283,8 @@ protected boolean checkEmptyKeyword(int parentShapeIndex, InternalSpatialDatatyp } if (!potentialEmptyKeyword.equals("")) { - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } return false; } @@ -1271,8 +1301,9 @@ private void incrementPointNumStartIfPointNotReused(int pointEndIndex) { * Helper used for resurcive iteration for constructing GeometryCollection in WKT form. * * @param shapeEndIndex . + * @throws SQLServerException */ - private void constructGeometryCollectionWKThelper(int shapeEndIndex) { + private void constructGeometryCollectionWKThelper(int shapeEndIndex) throws SQLServerException { //phase 1: assume that there is no multi - stuff and no geometrycollection while (currentShapeIndex < shapeEndIndex) { InternalSpatialDatatype isd = InternalSpatialDatatype.valueOf(shapes[currentShapeIndex].getOpenGISType()); @@ -1531,13 +1562,14 @@ private void skipFirstPointWkt() { } } - private void readComma() { + private void readComma() throws SQLServerException { skipWhiteSpaces(); if (wkt.charAt(currentWktPos) == ',') { currentWktPos++; skipWhiteSpaces(); } else { - throw new IllegalArgumentException("Illegal character at wkt position " + currentWktPos); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); + throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); } } 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 886cb36cc..36380fe95 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLServerSpatialDatatypeTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLServerSpatialDatatypeTest.java @@ -29,6 +29,7 @@ import com.microsoft.sqlserver.jdbc.Geography; import com.microsoft.sqlserver.jdbc.Geometry; import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.SQLServerException; import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; import com.microsoft.sqlserver.jdbc.SQLServerResultSet; import com.microsoft.sqlserver.jdbc.TestResource; @@ -53,7 +54,7 @@ public class SQLServerSpatialDatatypeTest extends AbstractTest { static boolean isDenaliOrLater = false; @Test - public void testPointWkb() throws DecoderException { + public void testPointWkb() throws DecoderException, SQLException { String geoWKT = "POINT(3 40 5 6)"; byte[] geomWKB = Hex.decodeHex("00000000010F0000000000000840000000000000444000000000000014400000000000001840".toCharArray()); byte[] geogWKB = Hex.decodeHex("E6100000010F0000000000004440000000000000084000000000000014400000000000001840".toCharArray()); @@ -64,7 +65,7 @@ public void testPointWkb() throws DecoderException { } @Test - public void testLineStringWkb() throws DecoderException { + public void testLineStringWkb() throws DecoderException, SQLException { String geoWKT = "LINESTRING(1 0, 0 1, -1 0)"; byte[] geomWKB = Hex.decodeHex("00000000010403000000000000000000F03F00000000000000000000000000000000000000000000F03F000000000000F0BF000000000000000001000000010000000001000000FFFFFFFF0000000002".toCharArray()); byte[] geogWKB = Hex.decodeHex("E61000000104030000000000000000000000000000000000F03F000000000000F03F00000000000000000000000000000000000000000000F0BF01000000010000000001000000FFFFFFFF0000000002".toCharArray()); @@ -75,7 +76,7 @@ public void testLineStringWkb() throws DecoderException { } @Test - public void testPolygonWkb() throws DecoderException { + public void testPolygonWkb() throws DecoderException, SQLException { String geoWKT = "POLYGON((0 0, 0 3, 3 3, 3 0, 0 0), (1 1, 1 2, 2 1, 1 1))"; byte[] geomWKB = Hex.decodeHex("000000000104090000000000000000000000000000000000000000000000000000000000000000000840000000000000084000000000000008400000000000000840000000000000000000000000000000000000000000000000000000000000F03F000000000000F03F000000000000F03F00000000000000400000000000000040000000000000F03F000000000000F03F000000000000F03F020000000200000000000500000001000000FFFFFFFF0000000003".toCharArray()); byte[] geogWKB = Hex.decodeHex("E61000000200090000000000000000000000000000000000000000000000000008400000000000000000000000000000084000000000000008400000000000000000000000000000084000000000000000000000000000000000000000000000F03F000000000000F03F0000000000000040000000000000F03F000000000000F03F0000000000000040000000000000F03F000000000000F03F020000000100000000010500000001000000FFFFFFFF0000000003".toCharArray()); @@ -86,7 +87,7 @@ public void testPolygonWkb() throws DecoderException { } @Test - public void testMultiPointWkb() throws DecoderException { + public void testMultiPointWkb() throws DecoderException, SQLException { String geoWKT = "MULTIPOINT((2 3), (7 8 9.5))"; byte[] geomWKB = Hex.decodeHex("00000000010502000000000000000000004000000000000008400000000000001C400000000000002040000000000000F8FF0000000000002340020000000100000000010100000003000000FFFFFFFF0000000004000000000000000001000000000100000001".toCharArray()); byte[] geogWKB = Hex.decodeHex("E61000000105020000000000000000000840000000000000004000000000000020400000000000001C40000000000000F8FF0000000000002340020000000100000000010100000003000000FFFFFFFF0000000004000000000000000001000000000100000001".toCharArray()); @@ -97,7 +98,7 @@ public void testMultiPointWkb() throws DecoderException { } @Test - public void testMultiLineStringWkb() throws DecoderException { + public void testMultiLineStringWkb() throws DecoderException, SQLException { String geoWKT = "MULTILINESTRING((0 2, 1 1), (1 0, 1 1))"; byte[] geomWKB = Hex.decodeHex("0000000001040400000000000000000000000000000000000040000000000000F03F000000000000F03F000000000000F03F0000000000000000000000000000F03F000000000000F03F020000000100000000010200000003000000FFFFFFFF0000000005000000000000000002000000000100000002".toCharArray()); byte[] geogWKB = Hex.decodeHex("E610000001040400000000000000000000400000000000000000000000000000F03F000000000000F03F0000000000000000000000000000F03F000000000000F03F000000000000F03F020000000100000000010200000003000000FFFFFFFF0000000005000000000000000002000000000100000002".toCharArray()); @@ -108,7 +109,7 @@ public void testMultiLineStringWkb() throws DecoderException { } @Test - public void testMultiPolygonWkb() throws DecoderException { + public void testMultiPolygonWkb() throws DecoderException, SQLException { String geoWKT = "MULTIPOLYGON(((1 1, 1 2, 2 1, 1 1), (0 0, 0 3, 3 3, 3 0, 0 0 7)), ((9 9, 9 10, 10 9, 9 9)))"; byte[] geomWKB = Hex.decodeHex("0000000001010D000000000000000000F03F000000000000F03F000000000000F03F00000000000000400000000000000040000000000000F03F000000000000F03F000000000000F03F000000000000000000000000000000000000000000000000000000000000084000000000000008400000000000000840000000000000084000000000000000000000000000000000000000000000000000000000000022400000000000002240000000000000224000000000000024400000000000002440000000000000224000000000000022400000000000002240000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF0000000000001C40000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF0300000002000000000004000000020900000003000000FFFFFFFF0000000006000000000000000003000000000200000003".toCharArray()); byte[] geogWKB = Hex.decodeHex("E610000002010D000000000000000000F03F000000000000F03F0000000000000040000000000000F03F000000000000F03F0000000000000040000000000000F03F000000000000F03F000000000000000000000000000000000000000000000840000000000000000000000000000008400000000000000840000000000000000000000000000008400000000000000000000000000000000000000000000022400000000000002240000000000000244000000000000022400000000000002240000000000000244000000000000022400000000000002240000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF0000000000001C40000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF0300000001000000000104000000010900000003000000FFFFFFFF0000000006000000000000000003000000000200000003".toCharArray()); @@ -119,7 +120,7 @@ public void testMultiPolygonWkb() throws DecoderException { } @Test - public void testGeometryCollectionWkb() throws DecoderException { + public void testGeometryCollectionWkb() throws DecoderException, SQLException { String geoWKT = "GEOMETRYCOLLECTION(POINT(3 3 1), LINESTRING(1 0, 0 1, -1 0), CIRCULARSTRING(1 3, 3 5, 4 7, 7 3, 1 3), POLYGON((0 0 2, 1 10 3, 1 0 4, 0 0 2), (0 0 2, 1 10 3, 1 0 4, 0 0 2), (0 0 2, 1 10 3, 1 0 4, 0 0 2)), MULTIPOINT((2 3), (7 8 9.5)), MULTILINESTRING((0 2, 1 1), (1 0, 1 1)), MULTIPOLYGON(((0 0, 0 3, 3 3, 3 0, 0 0), (1 1, 1 2, 2 1, 1 1)), ((9 9, 9 10, 10 9, 9 9))), COMPOUNDCURVE(CIRCULARSTRING(1 0 3, 0 1 3, 9 6 3, 8 7 3, -1 0 3), CIRCULARSTRING(-1 0 3, 7 9 3, -10 2 3), (-10 2 3, 77 77 77, 88 88 88, 2 6 4), (2 6 4, 3 3 6, 7 7 1)), CURVEPOLYGON((0 0, 0 0, 0 0, 0 0), COMPOUNDCURVE((0 -23.43778, 0 23.43778), CIRCULARSTRING(0 23.43778, -45 -23.43778, 0 -23.43778)), COMPOUNDCURVE((0 -23.43778, 7 7, 0 23.43778), CIRCULARSTRING(0 23.43778, 8 8, 8 8, -45 23.43778, -90 23.43778), (-90 23.43778, -90 -23.43778), CIRCULARSTRING(-90 -23.43778, -45 -23.43778, 0 -23.43778))), POLYGON((0 0 2, 1 10 3, 1 0 4, 0 0 2)))"; byte[] geomWKB = Hex.decodeHex("0100000002014A00000000000000000008400000000000000840000000000000F03F00000000000000000000000000000000000000000000F03F000000000000F0BF0000000000000000000000000000F03F00000000000008400000000000000840000000000000144000000000000010400000000000001C400000000000001C400000000000000840000000000000F03F000000000000084000000000000000000000000000000000000000000000F03F0000000000002440000000000000F03F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F03F0000000000002440000000000000F03F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F03F0000000000002440000000000000F03F000000000000000000000000000000000000000000000000000000000000004000000000000008400000000000001C40000000000000204000000000000000000000000000000040000000000000F03F000000000000F03F000000000000F03F0000000000000000000000000000F03F000000000000F03F0000000000000000000000000000000000000000000000000000000000000840000000000000084000000000000008400000000000000840000000000000000000000000000000000000000000000000000000000000F03F000000000000F03F000000000000F03F00000000000000400000000000000040000000000000F03F000000000000F03F000000000000F03F00000000000022400000000000002240000000000000224000000000000024400000000000002440000000000000224000000000000022400000000000002240000000000000F03F00000000000000000000000000000000000000000000F03F0000000000002240000000000000184000000000000020400000000000001C40000000000000F0BF00000000000000000000000000001C40000000000000224000000000000024C00000000000000040000000000040534000000000004053400000000000005640000000000000564000000000000000400000000000001840000000000000084000000000000008400000000000001C400000000000001C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C7D79E59127037C00000000000000000C7D79E591270374000000000008046C0C7D79E59127037C00000000000000000C7D79E59127037C00000000000000000C7D79E59127037C00000000000001C400000000000001C400000000000000000C7D79E5912703740000000000000204000000000000020400000000000002040000000000000204000000000008046C0C7D79E591270374000000000008056C0C7D79E591270374000000000008056C0C7D79E59127037C000000000008046C0C7D79E59127037C00000000000000000C7D79E59127037C000000000000000000000000000000000000000000000F03F0000000000002440000000000000F03F000000000000000000000000000000000000000000000000000000000000F03F000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000004000000000000008400000000000001040000000000000004000000000000000400000000000000840000000000000104000000000000000400000000000000040000000000000084000000000000010400000000000000040000000000000F8FF0000000000002340000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF00000000000008400000000000000840000000000000084000000000000008400000000000000840000000000000084000000000000008400000000000405340000000000000564000000000000010400000000000001840000000000000F03F000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF0000000000000040000000000000084000000000000010400000000000000040120000000100000000010100000002040000000109000000010D00000001110000000115000000011600000001170000000119000000011B00000001200000000124000000032800000001340000000338000000033C000000014600000011000000FFFFFFFF0000000007000000000000000001000000000100000002000000000200000008000000000300000003000000000600000004050000000600000001050000000700000001000000000800000005080000000800000002080000000900000002000000000A000000060B0000000A000000030B0000000C00000003000000000D00000009000000000E0000000A0000000011000000031000000003010302000002000203020003010203".toCharArray()); byte[] geogWKB = Hex.decodeHex("E610000002214A000000000000000000084000000000000008400000000000000000000000000000F03F000000000000F03F00000000000000000000000000000000000000000000F0BF0000000000000840000000000000F03F000000000000144000000000000008400000000000001C40000000000000104000000000000008400000000000001C400000000000000840000000000000F03F000000000000000000000000000000000000000000002440000000000000F03F0000000000000000000000000000F03F00000000000000000000000000000000000000000000000000000000000000000000000000002440000000000000F03F0000000000000000000000000000F03F00000000000000000000000000000000000000000000000000000000000000000000000000002440000000000000F03F0000000000000000000000000000F03F000000000000000000000000000000000000000000000840000000000000004000000000000020400000000000001C4000000000000000400000000000000000000000000000F03F000000000000F03F0000000000000000000000000000F03F000000000000F03F000000000000F03F0000000000000000000000000000000000000000000008400000000000000000000000000000084000000000000008400000000000000000000000000000084000000000000000000000000000000000000000000000F03F000000000000F03F0000000000000040000000000000F03F000000000000F03F0000000000000040000000000000F03F000000000000F03F000000000000224000000000000022400000000000002440000000000000224000000000000022400000000000002440000000000000224000000000000022400000000000000000000000000000F03F000000000000F03F0000000000000000000000000000184000000000000022400000000000001C4000000000000020400000000000000000000000000000F0BF00000000000022400000000000001C40000000000000004000000000000024C0000000000040534000000000004053400000000000005640000000000000564000000000000018400000000000000040000000000000084000000000000008400000000000001C400000000000001C4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C7D79E59127037C00000000000000000C7D79E59127037400000000000000000C7D79E59127037C000000000008046C0C7D79E59127037C00000000000000000C7D79E59127037C000000000000000000000000000001C400000000000001C40C7D79E591270374000000000000000000000000000002040000000000000204000000000000020400000000000002040C7D79E591270374000000000008046C0C7D79E591270374000000000008056C0C7D79E59127037C000000000008056C0C7D79E59127037C000000000008046C0C7D79E59127037C00000000000000000000000000000000000000000000000000000000000002440000000000000F03F0000000000000000000000000000F03F00000000000000000000000000000000000000000000F03F000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000004000000000000008400000000000001040000000000000004000000000000000400000000000000840000000000000104000000000000000400000000000000040000000000000084000000000000010400000000000000040000000000000F8FF0000000000002340000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF00000000000008400000000000000840000000000000084000000000000008400000000000000840000000000000084000000000000008400000000000405340000000000000564000000000000010400000000000001840000000000000F03F000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF0000000000000040000000000000084000000000000010400000000000000040120000000100000000010100000002040000000109000000010D00000001110000000115000000011600000001170000000119000000011B00000001200000000124000000032800000001340000000338000000033C000000014600000011000000FFFFFFFF0000000007000000000000000001000000000100000002000000000200000008000000000300000003000000000600000004050000000600000001050000000700000001000000000800000005080000000800000002080000000900000002000000000A000000060B0000000A000000030B0000000C00000003000000000D00000009000000000E0000000A0000000011000000031000000003010302000002000203020003010203".toCharArray()); @@ -130,7 +131,7 @@ public void testGeometryCollectionWkb() throws DecoderException { } @Test - public void testCircularStringWkb() throws DecoderException { + public void testCircularStringWkb() throws DecoderException, SQLException { String geoWKT = "CIRCULARSTRING(2 1 3 4, 1 2 3, 0 7 3, 1 0 3, 2 1 3)"; byte[] geomWKB = Hex.decodeHex("000000000207050000000000000000000040000000000000F03F000000000000F03F000000000000004000000000000000000000000000001C40000000000000F03F00000000000000000000000000000040000000000000F03F000000000000084000000000000008400000000000000840000000000000084000000000000008400000000000001040000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF01000000020000000001000000FFFFFFFF0000000008".toCharArray()); byte[] geogWKB = Hex.decodeHex("E6100000020705000000000000000000F03F00000000000000400000000000000040000000000000F03F0000000000001C4000000000000000000000000000000000000000000000F03F000000000000F03F0000000000000040000000000000084000000000000008400000000000000840000000000000084000000000000008400000000000001040000000000000F8FF000000000000F8FF000000000000F8FF000000000000F8FF01000000020000000001000000FFFFFFFF0000000008".toCharArray()); @@ -141,7 +142,7 @@ public void testCircularStringWkb() throws DecoderException { } @Test - public void testCompoundCurveWkb() throws DecoderException { + public void testCompoundCurveWkb() throws DecoderException, SQLException { String geoWKT = "COMPOUNDCURVE(CIRCULARSTRING(1 0 3, 0 1 3, 9 6 3, 8 7 3, -1 0 3), CIRCULARSTRING(-1 0 3, 7 9 3, -10 2 3), (-10 2 3, 77 77 77, 88 88 88, 2 6 4), (2 6 4, 3 3 6, 7 7 1))"; byte[] geomWKB = Hex.decodeHex("0000000002050C000000000000000000F03F00000000000000000000000000000000000000000000F03F0000000000002240000000000000184000000000000020400000000000001C40000000000000F0BF00000000000000000000000000001C40000000000000224000000000000024C00000000000000040000000000040534000000000004053400000000000005640000000000000564000000000000000400000000000001840000000000000084000000000000008400000000000001C400000000000001C4000000000000008400000000000000840000000000000084000000000000008400000000000000840000000000000084000000000000008400000000000405340000000000000564000000000000010400000000000001840000000000000F03F01000000030000000001000000FFFFFFFF0000000009080000000301030200000200".toCharArray()); byte[] geogWKB = Hex.decodeHex("E610000002050C0000000000000000000000000000000000F03F000000000000F03F0000000000000000000000000000184000000000000022400000000000001C4000000000000020400000000000000000000000000000F0BF00000000000022400000000000001C40000000000000004000000000000024C0000000000040534000000000004053400000000000005640000000000000564000000000000018400000000000000040000000000000084000000000000008400000000000001C400000000000001C4000000000000008400000000000000840000000000000084000000000000008400000000000000840000000000000084000000000000008400000000000405340000000000000564000000000000010400000000000001840000000000000F03F01000000030000000001000000FFFFFFFF0000000009080000000301030200000200".toCharArray()); @@ -152,7 +153,7 @@ public void testCompoundCurveWkb() throws DecoderException { } @Test - public void testCurvePolygonWkb() throws DecoderException { + public void testCurvePolygonWkb() throws DecoderException, SQLException { String geoWKT = "CURVEPOLYGON((0 0, 0 0, 0 0, 0 0), CIRCULARSTRING(1 3, 3 5, 4 7, 7 3, 1 3), COMPOUNDCURVE((0 -23.43778, 0 23.43778), CIRCULARSTRING(0 23.43778, -45 -23.43778, 0 -23.43778)), COMPOUNDCURVE((0 -23.43778, 7 7, 0 23.43778), CIRCULARSTRING(0 23.43778, 8 8, 8 8, -45 23.43778, -90 23.43778), (-90 23.43778, -90 -23.43778), CIRCULARSTRING(-90 -23.43778, -45 -23.43778, 0 -23.43778)))"; byte[] geomWKB = Hex.decodeHex("0A00000002001700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F03F00000000000008400000000000000840000000000000144000000000000010400000000000001C400000000000001C400000000000000840000000000000F03F00000000000008400000000000000000C7D79E59127037C00000000000000000C7D79E591270374000000000008046C0C7D79E59127037C00000000000000000C7D79E59127037C00000000000000000C7D79E59127037C00000000000001C400000000000001C400000000000000000C7D79E5912703740000000000000204000000000000020400000000000002040000000000000204000000000008046C0C7D79E591270374000000000008056C0C7D79E591270374000000000008056C0C7D79E59127037C000000000008046C0C7D79E59127037C00000000000000000C7D79E59127037C004000000010000000002040000000309000000030D00000001000000FFFFFFFF000000000A080000000203020003010203".toCharArray()); byte[] geogWKB = Hex.decodeHex("E6100000020017000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000840000000000000F03F000000000000144000000000000008400000000000001C40000000000000104000000000000008400000000000001C400000000000000840000000000000F03FC7D79E59127037C00000000000000000C7D79E59127037400000000000000000C7D79E59127037C000000000008046C0C7D79E59127037C00000000000000000C7D79E59127037C000000000000000000000000000001C400000000000001C40C7D79E591270374000000000000000000000000000002040000000000000204000000000000020400000000000002040C7D79E591270374000000000008046C0C7D79E591270374000000000008056C0C7D79E59127037C000000000008056C0C7D79E59127037C000000000008046C0C7D79E59127037C0000000000000000004000000010000000002040000000309000000030D00000001000000FFFFFFFF000000000A080000000203020003010203".toCharArray()); @@ -163,7 +164,7 @@ public void testCurvePolygonWkb() throws DecoderException { } @Test - public void testFullGlobeWkb() throws DecoderException { + public void testFullGlobeWkb() throws DecoderException, SQLException { String geoWKT = "FULLGLOBE"; byte[] geogWKB = Hex.decodeHex("E61000000224000000000000000001000000FFFFFFFFFFFFFFFF0B".toCharArray()); Geography geogWKT = Geography.deserialize(geogWKB); @@ -296,7 +297,7 @@ public void testFullGlobeWkt() throws SQLException { try { Geometry.STGeomFromText(geoWKT, 0); } - catch (IllegalArgumentException e) { + catch (SQLServerException e) { assertEquals(e.getMessage(), "Fullglobe is not supported for Geometry."); } @@ -329,8 +330,8 @@ public void testIllegalCases() throws SQLException { try { testWkt(geoWKT); } - catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "Reached unexpected end of WKT. Please make sure WKT is valid."); + catch (SQLServerException e) { + assertEquals(e.getMessage(), "Illegal Well-Known text. Please make sure Well-Known text is valid."); } //Not enough closing and opening bracket case @@ -339,8 +340,8 @@ public void testIllegalCases() throws SQLException { try { testWkt(geoWKT); } - catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")+"14"); + catch (SQLServerException e) { + assertEquals(e.getMessage(), "Illegal character in Well-Known text at position 14."); } //Too many closing bracket @@ -349,8 +350,8 @@ public void testIllegalCases() throws SQLException { try { testWkt(geoWKT); } - catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")+"91"); + catch (SQLServerException e) { + assertEquals(e.getMessage(), "Illegal character in Well-Known text at position 91."); } //Too many opening bracket @@ -359,8 +360,8 @@ public void testIllegalCases() throws SQLException { try { testWkt(geoWKT); } - catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")+"15"); + catch (SQLServerException e) { + assertEquals(e.getMessage(), "Illegal character in Well-Known text at position 15."); } //Too many coordinates @@ -369,8 +370,8 @@ public void testIllegalCases() throws SQLException { try { testWkt(geoWKT); } - catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")+"23"); + catch (SQLServerException e) { + assertEquals(e.getMessage(), "Illegal character in Well-Known text at position 23."); } //Too little coordinates @@ -379,8 +380,8 @@ public void testIllegalCases() throws SQLException { try { testWkt(geoWKT); } - catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")+"17"); + catch (SQLServerException e) { + assertEquals(e.getMessage(), "Illegal character in Well-Known text at position 17."); } //Incorrect data type @@ -389,8 +390,8 @@ public void testIllegalCases() throws SQLException { try { testWkt(geoWKT); } - catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")+"14"); + catch (SQLServerException e) { + assertEquals(e.getMessage(), "Illegal character in Well-Known text at position 14."); } // too many commas @@ -399,8 +400,8 @@ public void testIllegalCases() throws SQLException { try { testWkt(geoWKT); } - catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")+"35"); + catch (SQLServerException e) { + assertEquals(e.getMessage(), "Illegal character in Well-Known text at position 35."); } // too little commas @@ -409,8 +410,8 @@ public void testIllegalCases() throws SQLException { try { testWkt(geoWKT); } - catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")+"35"); + catch (SQLServerException e) { + assertEquals(e.getMessage(), "Illegal character in Well-Known text at position 35."); } } From c322174338b6b6e2dda20af8d4ec34dde170552e Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Wed, 13 Jun 2018 12:45:47 -0700 Subject: [PATCH 55/84] comment revisions --- .../sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java | 8 ++++---- .../sqlserver/jdbc/SQLServerBulkCSVFileRecord.java | 8 ++++---- .../com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java | 2 +- .../com/microsoft/sqlserver/jdbc/SQLServerResource.java | 4 ++-- .../preparedStatement/BatchExecutionWithBulkCopyTest.java | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java index 86b920bdd..5aa706af7 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java @@ -109,6 +109,7 @@ private Object convertValue(ColumnMetadata cm, case Types.INTEGER: { // Formatter to remove the decimal part as SQL Server floors the decimal in integer types DecimalFormat decimalFormatter = new DecimalFormat("#"); + decimalFormatter.setRoundingMode(RoundingMode.DOWN); String formatedfInput = decimalFormatter.format(Double.parseDouble(data.toString())); return Integer.valueOf(formatedfInput); } @@ -117,6 +118,7 @@ private Object convertValue(ColumnMetadata cm, case Types.SMALLINT: { // Formatter to remove the decimal part as SQL Server floors the decimal in integer types DecimalFormat decimalFormatter = new DecimalFormat("#"); + decimalFormatter.setRoundingMode(RoundingMode.DOWN); String formatedfInput = decimalFormatter.format(Double.parseDouble(data.toString())); return Short.valueOf(formatedfInput); } @@ -174,7 +176,6 @@ private Object convertValue(ColumnMetadata cm, case 2013: // java.sql.Types.TIME_WITH_TIMEZONE { - DriverJDBCVersion.checkSupportsJDBC42(); OffsetTime offsetTimeValue; // The per-column DateTimeFormatter gets priority. @@ -190,7 +191,6 @@ else if (timeFormatter != null) case 2014: // java.sql.Types.TIMESTAMP_WITH_TIMEZONE { - DriverJDBCVersion.checkSupportsJDBC42(); OffsetDateTime offsetDateTimeValue; // The per-column DateTimeFormatter gets priority. @@ -238,7 +238,7 @@ public Object[] getRowData() throws SQLServerException { // check if the size of the list of values = size of the list of columns (which is optional) if (null != columnList && columnList.size() != valueList.size()) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_CSVDataSchemaMismatch")); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_DataSchemaMismatch")); Object[] msgArgs = {}; throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); } @@ -308,7 +308,7 @@ else if (valueData.equalsIgnoreCase("null")) { throw new SQLServerException(form.format(new Object[] {value, JDBCType.of(pair.getValue().columnType)}), null, 0, e); } catch (ArrayIndexOutOfBoundsException e) { - throw new SQLServerException(SQLServerException.getErrString("R_CSVDataSchemaMismatch"), e); + throw new SQLServerException(SQLServerException.getErrString("R_DataSchemaMismatch"), e); } } return data; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java index 9840e2c24..e89e36a78 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java @@ -294,7 +294,7 @@ public Object[] getRowData() throws SQLServerException { // Source header has more columns than current line read if (columnNames != null && (columnNames.length > data.length)) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_CSVDataSchemaMismatch")); + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_DataSchemaMismatch")); Object[] msgArgs = {}; throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); } @@ -313,6 +313,7 @@ public Object[] getRowData() throws SQLServerException { case Types.INTEGER: { // Formatter to remove the decimal part as SQL Server floors the decimal in integer types DecimalFormat decimalFormatter = new DecimalFormat("#"); + decimalFormatter.setRoundingMode(RoundingMode.DOWN); String formatedfInput = decimalFormatter.format(Double.parseDouble(data[pair.getKey() - 1])); dataRow[pair.getKey() - 1] = Integer.valueOf(formatedfInput); break; @@ -322,6 +323,7 @@ public Object[] getRowData() throws SQLServerException { case Types.SMALLINT: { // Formatter to remove the decimal part as SQL Server floors the decimal in integer types DecimalFormat decimalFormatter = new DecimalFormat("#"); + decimalFormatter.setRoundingMode(RoundingMode.DOWN); String formatedfInput = decimalFormatter.format(Double.parseDouble(data[pair.getKey() - 1])); dataRow[pair.getKey() - 1] = Short.valueOf(formatedfInput); break; @@ -390,7 +392,6 @@ public Object[] getRowData() throws SQLServerException { case 2013: // java.sql.Types.TIME_WITH_TIMEZONE { - DriverJDBCVersion.checkSupportsJDBC42(); OffsetTime offsetTimeValue; // The per-column DateTimeFormatter gets priority. @@ -407,7 +408,6 @@ else if (timeFormatter != null) case 2014: // java.sql.Types.TIMESTAMP_WITH_TIMEZONE { - DriverJDBCVersion.checkSupportsJDBC42(); OffsetDateTime offsetDateTimeValue; // The per-column DateTimeFormatter gets priority. @@ -458,7 +458,7 @@ else if (dateTimeFormatter != null) MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_errorConvertingValue")); throw new SQLServerException(form.format(new Object[]{value, JDBCType.of(cm.columnType)}), null, 0, e); } catch (ArrayIndexOutOfBoundsException e) { - throw new SQLServerException(SQLServerException.getErrString("R_CSVDataSchemaMismatch"), e); + throw new SQLServerException(SQLServerException.getErrString("R_DataSchemaMismatch"), e); } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java index 669845b90..0c28dc3cb 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java @@ -289,7 +289,7 @@ protected void checkDuplicateColumnName(int positionInTable, // duplicate check is not performed in case of same positionInTable value if (null != entry && entry.getKey() != positionInTable) { if (null != entry.getValue() && colName.trim().equalsIgnoreCase(entry.getValue().columnName)) { - throw new SQLServerException(SQLServerException.getErrString("R_BulkCSVDataDuplicateColumn"), null); + throw new SQLServerException(SQLServerException.getErrString("R_BulkDataDuplicateColumn"), null); } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 2b6a9d982..17238eecd 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -227,8 +227,8 @@ protected Object[][] getContents() { {"R_invalidTransactionOption", "UseInternalTransaction option cannot be set to TRUE when used with a Connection object."}, {"R_invalidNegativeArg", "The {0} argument cannot be negative."}, {"R_BulkColumnMappingsIsEmpty", "Cannot perform bulk copy operation if the only mapping is an identity column and KeepIdentity is set to false."}, - {"R_CSVDataSchemaMismatch", "Source data does not match source schema."}, - {"R_BulkCSVDataDuplicateColumn", "Duplicate column names are not allowed."}, + {"R_DataSchemaMismatch", "Source data does not match source schema."}, + {"R_BulkDataDuplicateColumn", "Duplicate column names are not allowed."}, {"R_invalidColumnOrdinal", "Column {0} is invalid. Column number should be greater than zero."}, {"R_unsupportedEncoding", "The encoding {0} is not supported."}, {"R_UnexpectedDescribeParamFormat", "Internal error. The format of the resultset returned by sp_describe_parameter_encryption is invalid. One of the resultsets is missing."}, diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java index 377fb39af..86ed56170 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java @@ -189,7 +189,7 @@ public void testAllcolumns() throws Exception { pstmt.setFloat(6, (float) 123.45); pstmt.setString(7, "b"); pstmt.setString(8, "varc"); - pstmt.setString(9, "varcmax"); + pstmt.setString(9, "''"); pstmt.addBatch(); pstmt.executeBatch(); @@ -206,7 +206,7 @@ public void testAllcolumns() throws Exception { expected[5] = 123.45; expected[6] = "b"; expected[7] = "varc"; - expected[8] = "varcmax"; + expected[8] = "''"; rs.next(); for (int i=0; i < expected.length; i++) { From ba8fd5cd3b1250093b444c697cfe9489d73f0764 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Wed, 13 Jun 2018 16:10:15 -0700 Subject: [PATCH 56/84] use TestResource --- .../sqlserver/jdbc/TestResource.java | 3 +- .../SQLServerSpatialDatatypeTest.java | 36 ++++++++++++++----- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java index 4d6e59dc4..b67b49871 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java @@ -42,7 +42,8 @@ protected Object[][] getContents() { {"R_invalidErrorMessage", "Invalid Error Message: "}, {"R_expectedFailPassed", "Expected failure did not fail"}, {"R_dataTypeNotFound", "Cannot find data type"}, - {"R_illegalCharWkt", "Illegal character at wkt position "}, + {"R_illegalCharWktPosition", "Illegal character in Well-Known text at position {0}."}, + {"R_illegalCharWkt", "Illegal Well-Known text. Please make sure Well-Known text is valid."}, {"R_errorMessage", " Error message: "}, {"R_createDropViewFailed", "Create/drop view with preparedStatement failed!"}, {"R_createDropSchemaFailed", "Create/drop schema with preparedStatement failed!"}, 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 36380fe95..21d0e0624 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLServerSpatialDatatypeTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLServerSpatialDatatypeTest.java @@ -8,6 +8,7 @@ package com.microsoft.sqlserver.jdbc.datatypes; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.sql.DriverManager; @@ -15,6 +16,7 @@ import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; @@ -331,7 +333,7 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (SQLServerException e) { - assertEquals(e.getMessage(), "Illegal Well-Known text. Please make sure Well-Known text is valid."); + assertEquals(e.getMessage(), TestResource.getResource("R_illegalCharWkt")); } //Not enough closing and opening bracket case @@ -341,7 +343,9 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (SQLServerException e) { - assertEquals(e.getMessage(), "Illegal character in Well-Known text at position 14."); + MessageFormat form = new MessageFormat(TestResource.getResource("R_illegalCharWktPosition")); + Object[] msgArgs1 = {"14"}; + assertEquals(e.getMessage(), form.format(msgArgs1)); } //Too many closing bracket @@ -351,7 +355,9 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (SQLServerException e) { - assertEquals(e.getMessage(), "Illegal character in Well-Known text at position 91."); + MessageFormat form = new MessageFormat(TestResource.getResource("R_illegalCharWktPosition")); + Object[] msgArgs1 = {"91"}; + assertEquals(e.getMessage(), form.format(msgArgs1)); } //Too many opening bracket @@ -361,7 +367,9 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (SQLServerException e) { - assertEquals(e.getMessage(), "Illegal character in Well-Known text at position 15."); + MessageFormat form = new MessageFormat(TestResource.getResource("R_illegalCharWktPosition")); + Object[] msgArgs1 = {"15"}; + assertEquals(e.getMessage(), form.format(msgArgs1)); } //Too many coordinates @@ -371,7 +379,9 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (SQLServerException e) { - assertEquals(e.getMessage(), "Illegal character in Well-Known text at position 23."); + MessageFormat form = new MessageFormat(TestResource.getResource("R_illegalCharWktPosition")); + Object[] msgArgs1 = {"23"}; + assertEquals(e.getMessage(), form.format(msgArgs1)); } //Too little coordinates @@ -381,7 +391,9 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (SQLServerException e) { - assertEquals(e.getMessage(), "Illegal character in Well-Known text at position 17."); + MessageFormat form = new MessageFormat(TestResource.getResource("R_illegalCharWktPosition")); + Object[] msgArgs1 = {"17"}; + assertEquals(e.getMessage(), form.format(msgArgs1)); } //Incorrect data type @@ -391,7 +403,9 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (SQLServerException e) { - assertEquals(e.getMessage(), "Illegal character in Well-Known text at position 14."); + MessageFormat form = new MessageFormat(TestResource.getResource("R_illegalCharWktPosition")); + Object[] msgArgs1 = {"14"}; + assertEquals(e.getMessage(), form.format(msgArgs1)); } // too many commas @@ -401,7 +415,9 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (SQLServerException e) { - assertEquals(e.getMessage(), "Illegal character in Well-Known text at position 35."); + MessageFormat form = new MessageFormat(TestResource.getResource("R_illegalCharWktPosition")); + Object[] msgArgs1 = {"35"}; + assertEquals(e.getMessage(), form.format(msgArgs1)); } // too little commas @@ -411,7 +427,9 @@ public void testIllegalCases() throws SQLException { testWkt(geoWKT); } catch (SQLServerException e) { - assertEquals(e.getMessage(), "Illegal character in Well-Known text at position 35."); + MessageFormat form = new MessageFormat(TestResource.getResource("R_illegalCharWktPosition")); + Object[] msgArgs1 = {"35"}; + assertEquals(e.getMessage(), form.format(msgArgs1)); } } From b7135071d2ac7780037093e2c9e60d64a55d1198 Mon Sep 17 00:00:00 2001 From: rene-ye Date: Mon, 18 Jun 2018 10:09:37 -0700 Subject: [PATCH 57/84] test changes removed finals removed database creation tracking --- .../DatabaseMetaDataTest.java | 66 +++++++++---------- 1 file changed, 30 insertions(+), 36 deletions(-) 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 1e5599e99..ef4a698b9 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -187,32 +187,30 @@ public void testDBSchema() throws SQLException { */ @Test public void testDBSchemasForDashedCatalogName() throws SQLException { - final UUID id = UUID.randomUUID(); - final String testCatalog = "dash-catalog"+id; - final String testSchema = "some-schema"+id; - boolean dropDatabase = false; + UUID id = UUID.randomUUID(); + String testCatalog = "dash-catalog"+id; + String testSchema = "some-schema"+id; - try (final Connection dashConn = DriverManager.getConnection(connectionString); - final Statement dashStatement = dashConn.createStatement()) { + try (Connection dashConn = DriverManager.getConnection(connectionString); + Statement dashStatement = dashConn.createStatement()) { connection.createStatement().execute(String.format("CREATE DATABASE [%s]", testCatalog)); - dropDatabase = true; dashStatement.execute(String.format("USE [%s]", testCatalog)); dashStatement.execute(String.format("CREATE SCHEMA [%s]", testSchema)); - final DatabaseMetaData databaseMetaData = connection.getMetaData(); - final ResultSet rs = databaseMetaData.getSchemas(testCatalog, null); + DatabaseMetaData databaseMetaData = connection.getMetaData(); + ResultSet rs = databaseMetaData.getSchemas(testCatalog, null); - final MessageFormat schemaEmptyFormat = new MessageFormat(TestResource.getResource("R_nameEmpty")); - final Object[] schemaMsgArgs = {"Schema"}; + MessageFormat schemaEmptyFormat = new MessageFormat(TestResource.getResource("R_nameEmpty")); + Object[] schemaMsgArgs = {"Schema"}; boolean hasResults = false; boolean hasDashCatalogSchema = false; while (rs.next()) { hasResults = true; - final String schemaName = rs.getString(1); + String schemaName = rs.getString(1); assertTrue(!StringUtils.isEmpty(schemaName), schemaEmptyFormat.format(schemaMsgArgs)); - final String catalogName = rs.getString(2); + String catalogName = rs.getString(2); if (schemaName.equals(testSchema)) { hasDashCatalogSchema = true; assertEquals(catalogName, testCatalog); @@ -221,15 +219,14 @@ public void testDBSchemasForDashedCatalogName() throws SQLException { } } - final MessageFormat atLeastOneFoundFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); + MessageFormat atLeastOneFoundFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); assertTrue(hasResults, atLeastOneFoundFormat.format(schemaMsgArgs)); - final MessageFormat dashCatalogFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); + MessageFormat dashCatalogFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); assertTrue(hasDashCatalogSchema, dashCatalogFormat.format(new Object[] {testSchema})); } finally { - if (dropDatabase) { - connection.createStatement().execute(String.format("DROP DATABASE [%s]", testCatalog)); - } + connection.createStatement().execute("IF EXISTS (SELECT name FROM sys.databases WHERE name = N'" + testCatalog + "') " + + "DROP DATABASE [" + testCatalog + "]"); } } @@ -240,43 +237,40 @@ public void testDBSchemasForDashedCatalogName() throws SQLException { */ @Test public void testDBSchemasForDashedCatalogNameWithPattern() throws SQLException { - final UUID id = UUID.randomUUID(); - final String testCatalog = "dash-catalog"+id; - final String testSchema = "some-schema"+id; - boolean dropDatabase = false; + UUID id = UUID.randomUUID(); + String testCatalog = "dash-catalog"+id; + String testSchema = "some-schema"+id; - try (final Connection dashConn = DriverManager.getConnection(connectionString); - final Statement dashStatement = dashConn.createStatement()) { + try (Connection dashConn = DriverManager.getConnection(connectionString); + Statement dashStatement = dashConn.createStatement()) { connection.createStatement().execute(String.format("CREATE DATABASE [%s]", testCatalog)); - dropDatabase = true; dashStatement.execute(String.format("USE [%s]", testCatalog)); dashStatement.execute(String.format("CREATE SCHEMA [%s]", testSchema)); - final DatabaseMetaData databaseMetaData = connection.getMetaData(); - final ResultSet rs = databaseMetaData.getSchemas(testCatalog, "some-%"); + DatabaseMetaData databaseMetaData = connection.getMetaData(); + ResultSet rs = databaseMetaData.getSchemas(testCatalog, "some-%"); - final MessageFormat schemaEmptyFormat = new MessageFormat(TestResource.getResource("R_nameEmpty")); - final Object[] schemaMsgArgs = {testSchema}; - final Object[] catalogMsgArgs = {testCatalog}; + MessageFormat schemaEmptyFormat = new MessageFormat(TestResource.getResource("R_nameEmpty")); + Object[] schemaMsgArgs = {testSchema}; + Object[] catalogMsgArgs = {testCatalog}; boolean hasResults = false; while (rs.next()) { hasResults = true; - final String schemaName = rs.getString(1); - final String catalogName = rs.getString(2); + String schemaName = rs.getString(1); + String catalogName = rs.getString(2); assertTrue(!StringUtils.isEmpty(schemaName), schemaEmptyFormat.format(schemaMsgArgs)); assertTrue(!StringUtils.isEmpty(catalogName), schemaEmptyFormat.format(catalogMsgArgs)); assertEquals(schemaName, schemaMsgArgs[0]); assertEquals(catalogName, catalogMsgArgs[0]); } - final MessageFormat atLeastOneFoundFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); + MessageFormat atLeastOneFoundFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); assertTrue(hasResults, atLeastOneFoundFormat.format(schemaMsgArgs)); } finally { - if (dropDatabase) { - connection.createStatement().execute(String.format("DROP DATABASE [%s]", testCatalog)); - } + connection.createStatement().execute("IF EXISTS (SELECT name FROM sys.databases WHERE name = N'" + testCatalog + "') " + + "DROP DATABASE [" + testCatalog + "]"); } } From 876a375bc3022a934f7b0507628d57bf8fee2ce1 Mon Sep 17 00:00:00 2001 From: rene-ye Date: Mon, 18 Jun 2018 13:36:19 -0700 Subject: [PATCH 58/84] drop database before creating --- .../DatabaseMetaDataTest.java | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) 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 ef4a698b9..e4f2e9f1a 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -181,20 +181,23 @@ public void testDBSchema() throws SQLException { } /** - * Tests that the catalog parameter containing - is escaped by - * {@link SQLServerDatabaseMetaData#getSchemas(String catalog, String schemaPattern)}. + * Tests that the catalog parameter containing - is escaped by {@link SQLServerDatabaseMetaData#getSchemas(String catalog, String schemaPattern)}. + * * @throws SQLException */ @Test public void testDBSchemasForDashedCatalogName() throws SQLException { UUID id = UUID.randomUUID(); - String testCatalog = "dash-catalog"+id; - String testSchema = "some-schema"+id; + String testCatalog = "dash-catalog" + id; + String testSchema = "some-schema" + id; + String dropDBIfExists = "IF EXISTS (SELECT name FROM sys.databases WHERE name = N'" + testCatalog + "') " + "DROP DATABASE [" + testCatalog + + "]"; - try (Connection dashConn = DriverManager.getConnection(connectionString); - Statement dashStatement = dashConn.createStatement()) { + try (Connection dashConn = DriverManager.getConnection(connectionString); Statement dashStatement = dashConn.createStatement()) { + connection.createStatement().execute(dropDBIfExists); connection.createStatement().execute(String.format("CREATE DATABASE [%s]", testCatalog)); + dashStatement.execute(String.format("USE [%s]", testCatalog)); dashStatement.execute(String.format("CREATE SCHEMA [%s]", testSchema)); @@ -214,7 +217,8 @@ public void testDBSchemasForDashedCatalogName() throws SQLException { if (schemaName.equals(testSchema)) { hasDashCatalogSchema = true; assertEquals(catalogName, testCatalog); - } else { + } + else { assertNull(catalogName); } } @@ -224,27 +228,30 @@ public void testDBSchemasForDashedCatalogName() throws SQLException { MessageFormat dashCatalogFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); assertTrue(hasDashCatalogSchema, dashCatalogFormat.format(new Object[] {testSchema})); - } finally { - connection.createStatement().execute("IF EXISTS (SELECT name FROM sys.databases WHERE name = N'" + testCatalog + "') " + - "DROP DATABASE [" + testCatalog + "]"); + } + finally { + connection.createStatement().execute(dropDBIfExists); } } /** - * Tests that the catalog parameter containing - is escaped by - * {@link SQLServerDatabaseMetaData#getSchemas(String catalog, String schemaPattern)}. + * Tests that the catalog parameter containing - is escaped by {@link SQLServerDatabaseMetaData#getSchemas(String catalog, String schemaPattern)}. + * * @throws SQLException */ @Test public void testDBSchemasForDashedCatalogNameWithPattern() throws SQLException { UUID id = UUID.randomUUID(); - String testCatalog = "dash-catalog"+id; - String testSchema = "some-schema"+id; + String testCatalog = "dash-catalog" + id; + String testSchema = "some-schema" + id; + String dropDBIfExists = "IF EXISTS (SELECT name FROM sys.databases WHERE name = N'" + testCatalog + "') " + "DROP DATABASE [" + testCatalog + + "]"; - try (Connection dashConn = DriverManager.getConnection(connectionString); - Statement dashStatement = dashConn.createStatement()) { + try (Connection dashConn = DriverManager.getConnection(connectionString); Statement dashStatement = dashConn.createStatement()) { + connection.createStatement().execute(dropDBIfExists); connection.createStatement().execute(String.format("CREATE DATABASE [%s]", testCatalog)); + dashStatement.execute(String.format("USE [%s]", testCatalog)); dashStatement.execute(String.format("CREATE SCHEMA [%s]", testSchema)); @@ -268,9 +275,9 @@ public void testDBSchemasForDashedCatalogNameWithPattern() throws SQLException { MessageFormat atLeastOneFoundFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); assertTrue(hasResults, atLeastOneFoundFormat.format(schemaMsgArgs)); - } finally { - connection.createStatement().execute("IF EXISTS (SELECT name FROM sys.databases WHERE name = N'" + testCatalog + "') " + - "DROP DATABASE [" + testCatalog + "]"); + } + finally { + connection.createStatement().execute(dropDBIfExists); } } From 4c850493bc4ee2529767fce8507f0675af6c5b3f Mon Sep 17 00:00:00 2001 From: rene-ye Date: Mon, 18 Jun 2018 13:42:56 -0700 Subject: [PATCH 59/84] replaced dropDBIfExists with Utils function --- .../DatabaseMetaDataTest.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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 e4f2e9f1a..afb0547b2 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -190,13 +190,12 @@ public void testDBSchemasForDashedCatalogName() throws SQLException { UUID id = UUID.randomUUID(); String testCatalog = "dash-catalog" + id; String testSchema = "some-schema" + id; - String dropDBIfExists = "IF EXISTS (SELECT name FROM sys.databases WHERE name = N'" + testCatalog + "') " + "DROP DATABASE [" + testCatalog - + "]"; + Statement stmt = connection.createStatement(); try (Connection dashConn = DriverManager.getConnection(connectionString); Statement dashStatement = dashConn.createStatement()) { - connection.createStatement().execute(dropDBIfExists); - connection.createStatement().execute(String.format("CREATE DATABASE [%s]", testCatalog)); + Utils.dropDatabaseIfExists(testCatalog, stmt); + stmt.execute(String.format("CREATE DATABASE [%s]", testCatalog)); dashStatement.execute(String.format("USE [%s]", testCatalog)); dashStatement.execute(String.format("CREATE SCHEMA [%s]", testSchema)); @@ -230,8 +229,9 @@ public void testDBSchemasForDashedCatalogName() throws SQLException { assertTrue(hasDashCatalogSchema, dashCatalogFormat.format(new Object[] {testSchema})); } finally { - connection.createStatement().execute(dropDBIfExists); + Utils.dropDatabaseIfExists(testCatalog, stmt); } + stmt.close(); } /** @@ -244,13 +244,12 @@ public void testDBSchemasForDashedCatalogNameWithPattern() throws SQLException { UUID id = UUID.randomUUID(); String testCatalog = "dash-catalog" + id; String testSchema = "some-schema" + id; - String dropDBIfExists = "IF EXISTS (SELECT name FROM sys.databases WHERE name = N'" + testCatalog + "') " + "DROP DATABASE [" + testCatalog - + "]"; + Statement stmt = connection.createStatement(); try (Connection dashConn = DriverManager.getConnection(connectionString); Statement dashStatement = dashConn.createStatement()) { - connection.createStatement().execute(dropDBIfExists); - connection.createStatement().execute(String.format("CREATE DATABASE [%s]", testCatalog)); + Utils.dropDatabaseIfExists(testCatalog, stmt); + stmt.execute(String.format("CREATE DATABASE [%s]", testCatalog)); dashStatement.execute(String.format("USE [%s]", testCatalog)); dashStatement.execute(String.format("CREATE SCHEMA [%s]", testSchema)); @@ -277,8 +276,9 @@ public void testDBSchemasForDashedCatalogNameWithPattern() throws SQLException { assertTrue(hasResults, atLeastOneFoundFormat.format(schemaMsgArgs)); } finally { - connection.createStatement().execute(dropDBIfExists); + Utils.dropDatabaseIfExists(testCatalog, stmt); } + stmt.close(); } /** From fc7817741224a07ce7b629b1aba0a1bfd281a00e Mon Sep 17 00:00:00 2001 From: rene-ye Date: Mon, 18 Jun 2018 15:44:21 -0700 Subject: [PATCH 60/84] added try-with-resources nest avoid manually closing statements, and safetly handles resources. --- .../DatabaseMetaDataTest.java | 128 +++++++++--------- 1 file changed, 64 insertions(+), 64 deletions(-) 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 afb0547b2..403ccfd79 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -190,48 +190,48 @@ public void testDBSchemasForDashedCatalogName() throws SQLException { UUID id = UUID.randomUUID(); String testCatalog = "dash-catalog" + id; String testSchema = "some-schema" + id; - Statement stmt = connection.createStatement(); - try (Connection dashConn = DriverManager.getConnection(connectionString); Statement dashStatement = dashConn.createStatement()) { - - Utils.dropDatabaseIfExists(testCatalog, stmt); - stmt.execute(String.format("CREATE DATABASE [%s]", testCatalog)); - - dashStatement.execute(String.format("USE [%s]", testCatalog)); - dashStatement.execute(String.format("CREATE SCHEMA [%s]", testSchema)); - - DatabaseMetaData databaseMetaData = connection.getMetaData(); - ResultSet rs = databaseMetaData.getSchemas(testCatalog, null); - - MessageFormat schemaEmptyFormat = new MessageFormat(TestResource.getResource("R_nameEmpty")); - Object[] schemaMsgArgs = {"Schema"}; - - boolean hasResults = false; - boolean hasDashCatalogSchema = false; - while (rs.next()) { - hasResults = true; - String schemaName = rs.getString(1); - assertTrue(!StringUtils.isEmpty(schemaName), schemaEmptyFormat.format(schemaMsgArgs)); - String catalogName = rs.getString(2); - if (schemaName.equals(testSchema)) { - hasDashCatalogSchema = true; - assertEquals(catalogName, testCatalog); - } - else { - assertNull(catalogName); + try (Statement stmt = connection.createStatement()) { + try (Connection dashConn = DriverManager.getConnection(connectionString); Statement dashStatement = dashConn.createStatement()) { + + Utils.dropDatabaseIfExists(testCatalog, stmt); + stmt.execute(String.format("CREATE DATABASE [%s]", testCatalog)); + + dashStatement.execute(String.format("USE [%s]", testCatalog)); + dashStatement.execute(String.format("CREATE SCHEMA [%s]", testSchema)); + + DatabaseMetaData databaseMetaData = connection.getMetaData(); + ResultSet rs = databaseMetaData.getSchemas(testCatalog, null); + + MessageFormat schemaEmptyFormat = new MessageFormat(TestResource.getResource("R_nameEmpty")); + Object[] schemaMsgArgs = {"Schema"}; + + boolean hasResults = false; + boolean hasDashCatalogSchema = false; + while (rs.next()) { + hasResults = true; + String schemaName = rs.getString(1); + assertTrue(!StringUtils.isEmpty(schemaName), schemaEmptyFormat.format(schemaMsgArgs)); + String catalogName = rs.getString(2); + if (schemaName.equals(testSchema)) { + hasDashCatalogSchema = true; + assertEquals(catalogName, testCatalog); + } + else { + assertNull(catalogName); + } } - } - MessageFormat atLeastOneFoundFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); - assertTrue(hasResults, atLeastOneFoundFormat.format(schemaMsgArgs)); + MessageFormat atLeastOneFoundFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); + assertTrue(hasResults, atLeastOneFoundFormat.format(schemaMsgArgs)); - MessageFormat dashCatalogFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); - assertTrue(hasDashCatalogSchema, dashCatalogFormat.format(new Object[] {testSchema})); - } - finally { - Utils.dropDatabaseIfExists(testCatalog, stmt); + MessageFormat dashCatalogFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); + assertTrue(hasDashCatalogSchema, dashCatalogFormat.format(new Object[] {testSchema})); + } + finally { + Utils.dropDatabaseIfExists(testCatalog, stmt); + } } - stmt.close(); } /** @@ -244,41 +244,41 @@ public void testDBSchemasForDashedCatalogNameWithPattern() throws SQLException { UUID id = UUID.randomUUID(); String testCatalog = "dash-catalog" + id; String testSchema = "some-schema" + id; - Statement stmt = connection.createStatement(); - try (Connection dashConn = DriverManager.getConnection(connectionString); Statement dashStatement = dashConn.createStatement()) { + try (Statement stmt = connection.createStatement()) { + try (Connection dashConn = DriverManager.getConnection(connectionString); Statement dashStatement = dashConn.createStatement()) { - Utils.dropDatabaseIfExists(testCatalog, stmt); - stmt.execute(String.format("CREATE DATABASE [%s]", testCatalog)); + Utils.dropDatabaseIfExists(testCatalog, stmt); + stmt.execute(String.format("CREATE DATABASE [%s]", testCatalog)); - dashStatement.execute(String.format("USE [%s]", testCatalog)); - dashStatement.execute(String.format("CREATE SCHEMA [%s]", testSchema)); + dashStatement.execute(String.format("USE [%s]", testCatalog)); + dashStatement.execute(String.format("CREATE SCHEMA [%s]", testSchema)); - DatabaseMetaData databaseMetaData = connection.getMetaData(); - ResultSet rs = databaseMetaData.getSchemas(testCatalog, "some-%"); + DatabaseMetaData databaseMetaData = connection.getMetaData(); + ResultSet rs = databaseMetaData.getSchemas(testCatalog, "some-%"); - MessageFormat schemaEmptyFormat = new MessageFormat(TestResource.getResource("R_nameEmpty")); - Object[] schemaMsgArgs = {testSchema}; - Object[] catalogMsgArgs = {testCatalog}; + MessageFormat schemaEmptyFormat = new MessageFormat(TestResource.getResource("R_nameEmpty")); + Object[] schemaMsgArgs = {testSchema}; + Object[] catalogMsgArgs = {testCatalog}; - boolean hasResults = false; - while (rs.next()) { - hasResults = true; - String schemaName = rs.getString(1); - String catalogName = rs.getString(2); - assertTrue(!StringUtils.isEmpty(schemaName), schemaEmptyFormat.format(schemaMsgArgs)); - assertTrue(!StringUtils.isEmpty(catalogName), schemaEmptyFormat.format(catalogMsgArgs)); - assertEquals(schemaName, schemaMsgArgs[0]); - assertEquals(catalogName, catalogMsgArgs[0]); - } + boolean hasResults = false; + while (rs.next()) { + hasResults = true; + String schemaName = rs.getString(1); + String catalogName = rs.getString(2); + assertTrue(!StringUtils.isEmpty(schemaName), schemaEmptyFormat.format(schemaMsgArgs)); + assertTrue(!StringUtils.isEmpty(catalogName), schemaEmptyFormat.format(catalogMsgArgs)); + assertEquals(schemaName, schemaMsgArgs[0]); + assertEquals(catalogName, catalogMsgArgs[0]); + } - MessageFormat atLeastOneFoundFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); - assertTrue(hasResults, atLeastOneFoundFormat.format(schemaMsgArgs)); - } - finally { - Utils.dropDatabaseIfExists(testCatalog, stmt); + MessageFormat atLeastOneFoundFormat = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); + assertTrue(hasResults, atLeastOneFoundFormat.format(schemaMsgArgs)); + } + finally { + Utils.dropDatabaseIfExists(testCatalog, stmt); + } } - stmt.close(); } /** From 7dd2d4208cd590c265f458170fd0c64210a48999 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 19 Jun 2018 16:39:35 -0700 Subject: [PATCH 61/84] Fixing logic / adding more tests --- .../jdbc/SQLServerBulkBatchInsertRecord.java | 7 +- .../sqlserver/jdbc/SQLServerBulkCopy.java | 7 +- .../jdbc/SQLServerPreparedStatement.java | 106 ++++++------- .../sqlserver/jdbc/SQLServerStatement.java | 13 +- .../BatchExecutionWithBulkCopyTest.java | 147 +++++++++++++++--- 5 files changed, 188 insertions(+), 92 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java index 5aa706af7..3d366e5e5 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java @@ -18,6 +18,7 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map.Entry; import java.util.Set; @@ -27,10 +28,10 @@ */ public class SQLServerBulkBatchInsertRecord extends SQLServerBulkCommon implements ISQLServerBulkRecord, java.lang.AutoCloseable { - private ArrayList batchParam; + private List batchParam; private int batchParamIndex = -1; - private ArrayList columnList; - private ArrayList valueList; + private List columnList; + private List valueList; public SQLServerBulkBatchInsertRecord(ArrayList batchParam, ArrayList columnList, diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java index ab1bdb089..647d77f64 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java @@ -1495,7 +1495,12 @@ private String createInsertBulkCommand(TDSWriter tdsWriter) throws SQLServerExce if (null != destType && (destType.toLowerCase(Locale.ENGLISH).trim().startsWith("char") || destType.toLowerCase(Locale.ENGLISH).trim().startsWith("varchar"))) addCollate = " COLLATE " + columnCollation; } - bulkCmd.append("[" + colMapping.destinationColumnName + "] " + destType + addCollate + endColumn); + if (colMapping.destinationColumnName.contains("]")) { + String escapedColumnName = colMapping.destinationColumnName.replaceAll("]", "]]"); + bulkCmd.append("[" + escapedColumnName + "] " + destType + addCollate + endColumn); + } else { + bulkCmd.append("[" + colMapping.destinationColumnName + "] " + destType + addCollate + endColumn); + } } if (true == copyOptions.isCheckConstraints()) { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 4182efca5..72d234145 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -112,7 +112,7 @@ private void setPreparedStatementHandle(int handle) { */ private boolean useBulkCopyForBatchInsert; - /** Sets the prepared statement's useBulkCopyForBatchInsert value. + /** Gets the prepared statement's useBulkCopyForBatchInsert value. * * @return * Per the description. @@ -123,7 +123,7 @@ public boolean getUseBulkCopyForBatchInsert() throws SQLServerException { return useBulkCopyForBatchInsert; } - /** Fetches the prepared statement's useBulkCopyForBatchInsert value. + /** Sets the prepared statement's useBulkCopyForBatchInsert value. * * @throws SQLServerException when an error occurs */ @@ -2487,13 +2487,14 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL return updateCounts; } - String tableName = parseUserSQLForTableNameDW(false, false); + String tableName = parseUserSQLForTableNameDW(false, false, false, false); ArrayList columnList = parseUserSQLForColumnListDW(); ArrayList valueList = parseUserSQLForValueListDW(false); String destinationTableName = tableName; SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, connection.getHoldability(), stmtColumnEncriptionSetting); + // Get destination metadata try (SQLServerResultSet rs = stmt .executeQueryInternal("sp_executesql N'SET FMTONLY ON SELECT * FROM " + destinationTableName + " '");) { @@ -2649,33 +2650,49 @@ public long[] executeLargeBatch() throws SQLServerException, BatchUpdateExceptio } - private String parseUserSQLForTableNameDW(boolean hasInsertBeenFound, boolean hasIntoBeenFound) { + private String parseUserSQLForTableNameDW(boolean hasInsertBeenFound, boolean hasIntoBeenFound, boolean hasTableBeenFound, + boolean isExpectingTableName) { // As far as finding the table name goes, There are two cases: // Insert into and Insert // And there could be in-line comments (with /* and */) in between. // This method assumes the localUserSQL string starts with "insert". localUserSQL = localUserSQL.trim(); if (checkAndRemoveComments()) { - return parseUserSQLForTableNameDW(hasInsertBeenFound, hasIntoBeenFound); + return parseUserSQLForTableNameDW(hasInsertBeenFound, hasIntoBeenFound, hasTableBeenFound, isExpectingTableName); + } + + StringBuilder sb = new StringBuilder(); + + // If table has been found and the next character is not a . at this point, we've finished parsing the table name. + // This if statement is needed to handle the case where the user has something like: + // [dbo] . /* random comment */ [tableName] + if (hasTableBeenFound && !isExpectingTableName) { + if (localUserSQL.substring(0, 1).equalsIgnoreCase(".")) { + sb.append("."); + localUserSQL = localUserSQL.substring(1); + return sb.toString() + parseUserSQLForTableNameDW(true, true, true, true); + } else { + return ""; + } } if (localUserSQL.substring(0, 6).equalsIgnoreCase("insert") && !hasInsertBeenFound) { localUserSQL = localUserSQL.substring(6); - return parseUserSQLForTableNameDW(true, hasIntoBeenFound); + return parseUserSQLForTableNameDW(true, hasIntoBeenFound, hasTableBeenFound, isExpectingTableName); } if (localUserSQL.substring(0, 4).equalsIgnoreCase("into") && !hasIntoBeenFound) { // is it really "into"? // if the "into" is followed by a blank space or /*, then yes. - if (localUserSQL.charAt(4) == ' ' || localUserSQL.charAt(4) == '\t' || + if (Character.isWhitespace(localUserSQL.charAt(4)) || (localUserSQL.charAt(4) == '/' && localUserSQL.charAt(5) == '*')) { localUserSQL = localUserSQL.substring(4); - return parseUserSQLForTableNameDW(hasInsertBeenFound, true); + return parseUserSQLForTableNameDW(hasInsertBeenFound, true, hasTableBeenFound, isExpectingTableName); } // otherwise, we found the token that either contains the databasename.tablename or tablename. // Recursively handle this, but into has been found. (or rather, it's absent in the query - the "into" keyword is optional) - return parseUserSQLForTableNameDW(hasInsertBeenFound, true); + return parseUserSQLForTableNameDW(hasInsertBeenFound, true, hasTableBeenFound, isExpectingTableName); } // At this point, the next token has to be the table name. @@ -2687,24 +2704,14 @@ private String parseUserSQLForTableNameDW(boolean hasInsertBeenFound, boolean ha // keep checking if it's escaped while (localUserSQL.charAt(tempint + 1) == ']') { - localUserSQL = localUserSQL.substring(0, tempint) + localUserSQL.substring(tempint + 1); - tempint = localUserSQL.indexOf("]", tempint + 1); + tempint = localUserSQL.indexOf("]", tempint + 2); } // we've found a ] that is actually trying to close the square bracket. - // If it's followed by a dot, then it's a database. - // Otherwise, it's the table. - if (localUserSQL.charAt(tempint + 1) == '.') { - String tempstr = localUserSQL.substring(1, tempint); - localUserSQL = localUserSQL.substring(tempint + 2); - // assume that "INSERT" and "INTO" has been found, since we're at the table part already. - return tempstr + "." + parseUserSQLForTableNameDW(true, true); - } else { - // return tablename - String tempstr = localUserSQL.substring(1, tempint); - localUserSQL = localUserSQL.substring(tempint + 1); - return tempstr; - } + // return tablename + potentially more that's part of the table name + sb.append(localUserSQL.substring(0, tempint + 1)); + localUserSQL = localUserSQL.substring(tempint + 1); + return sb.toString() + parseUserSQLForTableNameDW(true, true, true, false); } // do the same for "" @@ -2713,36 +2720,21 @@ private String parseUserSQLForTableNameDW(boolean hasInsertBeenFound, boolean ha // keep checking if it's escaped while (localUserSQL.charAt(tempint + 1) == '\"') { - localUserSQL = localUserSQL.substring(0, tempint) + localUserSQL.substring(tempint + 1); - tempint = localUserSQL.indexOf("\"", tempint + 1); + tempint = localUserSQL.indexOf("\"", tempint + 2); } // we've found a " that is actually trying to close the quote. - // If it's followed by a dot, then it's a database. - // Otherwise, it's the table. - if (localUserSQL.charAt(tempint + 1) == '.') { - String tempstr = localUserSQL.substring(1, tempint); - localUserSQL = localUserSQL.substring(tempint + 2); - return tempstr + "." + parseUserSQLForTableNameDW(true, true); - } else { - // return tablename - String tempstr = localUserSQL.substring(1, tempint); - localUserSQL = localUserSQL.substring(tempint + 1); - return tempstr; - } + // return tablename + potentially more that's part of the table name + sb.append(localUserSQL.substring(0, tempint + 1)); + localUserSQL = localUserSQL.substring(tempint + 1); + return sb.toString() + parseUserSQLForTableNameDW(true, true, true, false); } - // At this point, the next chunk of string is the table name (could have database name), without starting with [ or ". - StringBuilder sb = new StringBuilder(); + // At this point, the next chunk of string is the table name, without starting with [ or ". while (localUserSQL.length() > 0) { - if (localUserSQL.charAt(0) == '.' || localUserSQL.charAt(0) == ' ' || localUserSQL.charAt(0) == '\t' - || localUserSQL.charAt(0) == '(') { - if (localUserSQL.charAt(0) == '.') { - localUserSQL = localUserSQL.substring(1); - return sb.toString() + "." + parseUserSQLForTableNameDW(true, true); - } else { - return sb.toString(); - } + // Keep going until the end of the table name is signalled - either a ., whitespace, or comment is encountered + if (localUserSQL.charAt(0) == '.' || Character.isWhitespace(localUserSQL.charAt(0)) || checkAndRemoveComments()) { + return sb.toString() + parseUserSQLForTableNameDW(true, true, true, false); } else { sb.append(localUserSQL.charAt(0)); localUserSQL = localUserSQL.substring(1); @@ -2823,16 +2815,14 @@ private ArrayList parseUserSQLForColumnListDWHelper(ArrayList li // At this point, the next chunk of string is the column name, without starting with [ or ". StringBuilder sb = new StringBuilder(); while (localUserSQL.length() > 0) { - if (localUserSQL.charAt(0) == ',' || localUserSQL.charAt(0) == ')') { - if (localUserSQL.charAt(0) == ',') { - localUserSQL = localUserSQL.substring(1); - listOfColumns.add(sb.toString()); - return parseUserSQLForColumnListDWHelper(listOfColumns); - } else { - localUserSQL = localUserSQL.substring(1); - listOfColumns.add(sb.toString()); - return listOfColumns; - } + if (localUserSQL.charAt(0) == ',') { + localUserSQL = localUserSQL.substring(1); + listOfColumns.add(sb.toString()); + return parseUserSQLForColumnListDWHelper(listOfColumns); + } else if (localUserSQL.charAt(0) == ')'){ + localUserSQL = localUserSQL.substring(1); + listOfColumns.add(sb.toString()); + return listOfColumns; } else if (checkAndRemoveComments()) { localUserSQL = localUserSQL.trim(); } else { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index 9566552fb..592bb8deb 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -968,16 +968,16 @@ final void resetForReexecute() throws SQLServerException { * * @param sql * The statment SQL. - * @return True is the statement is a select. + * @return True if the statement is a select. */ /* L0 */ final boolean isSelect(String sql) throws SQLServerException { checkClosed(); // Used to check just the first letter which would cause // "Set" commands to return true... String temp = sql.trim(); - char c = temp.charAt(0); - if (c != 's' && c != 'S') + if (null == sql || sql.length() < 6) { return false; + } return temp.substring(0, 6).equalsIgnoreCase("select"); } @@ -986,23 +986,20 @@ final void resetForReexecute() throws SQLServerException { * * @param sql * The statment SQL. - * @return True is the statement is a select. + * @return True if the statement is an insert. */ /* L0 */ final boolean isInsert(String sql) throws SQLServerException { checkClosed(); // Used to check just the first letter which would cause // "Set" commands to return true... String temp = sql.trim(); - if (null == sql || sql.length() < 3) { + if (null == sql || sql.length() < 6) { return false; } if (temp.substring(0, 2).equalsIgnoreCase("/*")) { int index = temp.indexOf("*/") + 2; return isInsert(temp.substring(index)); } - char c = temp.charAt(0); - if (c != 'i' && c != 'I') - return false; return temp.substring(0, 6).equalsIgnoreCase("insert"); } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java index 86ed56170..39b04aea1 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java @@ -40,10 +40,13 @@ public class BatchExecutionWithBulkCopyTest extends AbstractTest { static SQLServerPreparedStatement pstmt = null; static Statement stmt = null; static Connection connection = null; - static String tableName = "BulkCopyParseTest" + System.currentTimeMillis(); + static long UUID = System.currentTimeMillis();; + static String tableName = "BulkCopyParseTest" + UUID; + static String squareBracketTableName = "[peter]]]]test" + UUID + "]"; + static String doubleQuoteTableName = "\"peter\"\"\"\"test" + UUID + "\""; @Test - public void testIsInsert() throws SQLException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + public void testIsInsert() throws Exception { String valid1 = "INSERT INTO PeterTable values (1, 2)"; String valid2 = " INSERT INTO PeterTable values (1, 2)"; String valid3 = "/* asdf */ INSERT INTO PeterTable values (1, 2)"; @@ -59,7 +62,7 @@ public void testIsInsert() throws SQLException, NoSuchMethodException, SecurityE } @Test - public void testComments() throws SQLException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + public void testComments() throws Exception { pstmt = (SQLServerPreparedStatement) connection.prepareStatement(""); String valid = "/* rando comment *//* rando comment */ INSERT /* rando comment */ INTO /* rando comment *//*rando comment*/ PeterTable /*rando comment */" @@ -69,14 +72,14 @@ public void testComments() throws SQLException, NoSuchFieldException, SecurityEx f1.setAccessible(true); f1.set(pstmt, valid); - Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class); + Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class, boolean.class, boolean.class); method.setAccessible(true); - assertEquals((String) method.invoke(pstmt, false, false), "PeterTable"); + assertEquals((String) method.invoke(pstmt, false, false, false, false), "PeterTable"); } @Test - public void testBrackets() throws SQLException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + public void testBrackets() throws Exception { pstmt = (SQLServerPreparedStatement) connection.prepareStatement(""); String valid = "/* rando comment *//* rando comment */ INSERT /* rando comment */ INTO /* rando comment *//*rando comment*/ [Peter[]]Table] /*rando comment */" @@ -86,14 +89,14 @@ public void testBrackets() throws SQLException, NoSuchFieldException, SecurityEx f1.setAccessible(true); f1.set(pstmt, valid); - Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class); + Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class, boolean.class, boolean.class); method.setAccessible(true); - assertEquals((String) method.invoke(pstmt, false, false), "Peter[]Table"); + assertEquals((String) method.invoke(pstmt, false, false, false, false), "[Peter[]]Table]"); } @Test - public void testDoubleQuotes() throws SQLException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + public void testDoubleQuotes() throws Exception { pstmt = (SQLServerPreparedStatement) connection.prepareStatement(""); String valid = "/* rando comment *//* rando comment */ INSERT /* rando comment */ INTO /* rando comment *//*rando comment*/ \"Peter\"\"\"\"Table\" /*rando comment */" @@ -103,14 +106,14 @@ public void testDoubleQuotes() throws SQLException, NoSuchFieldException, Securi f1.setAccessible(true); f1.set(pstmt, valid); - Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class); + Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class, boolean.class, boolean.class); method.setAccessible(true); - assertEquals((String) method.invoke(pstmt, false, false), "Peter\"\"Table"); + assertEquals((String) method.invoke(pstmt, false, false, false, false), "\"Peter\"\"\"\"Table\""); } @Test - public void testAll() throws SQLException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + public void testAll() throws Exception { pstmt = (SQLServerPreparedStatement) connection.prepareStatement(""); String valid = "/* rando comment *//* rando comment */ INSERT /* rando comment */ INTO /* rando comment *//*rando comment*/ \"Peter\"\"\"\"Table\" /*rando comment */" @@ -121,10 +124,10 @@ public void testAll() throws SQLException, NoSuchFieldException, SecurityExcepti f1.setAccessible(true); f1.set(pstmt, valid); - Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class); + Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class, boolean.class, boolean.class); method.setAccessible(true); - assertEquals((String) method.invoke(pstmt, false, false), "Peter\"\"Table"); + assertEquals((String) method.invoke(pstmt, false, false, false, false), "\"Peter\"\"\"\"Table\""); method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForColumnListDW"); method.setAccessible(true); @@ -174,8 +177,8 @@ public void testAllcolumns() throws Exception { + "?, " + ")"; - SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); - SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); + pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); + stmt = (SQLServerStatement) connection.createStatement(); Timestamp myTimestamp = new Timestamp(114550L); @@ -228,8 +231,8 @@ public void testMixColumns() throws Exception { + "?, " + ")"; - SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); - SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); + pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); + stmt = (SQLServerStatement) connection.createStatement(); Timestamp myTimestamp = new Timestamp(114550L); @@ -282,8 +285,8 @@ public void testNullOrEmptyColumns() throws Exception { + "?, " + ")"; - SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); - SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); + pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); + stmt = (SQLServerStatement) connection.createStatement(); pstmt.setInt(1, 1234); pstmt.setBoolean(2, false); @@ -316,10 +319,108 @@ public void testNullOrEmptyColumns() throws Exception { } } + @Test + public void testSquareBracketAgainstDB() throws Exception { + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); + f1.setAccessible(true); + f1.set(connection, true); + stmt = (SQLServerStatement) connection.createStatement(); + + Utils.dropTableIfExists(squareBracketTableName, stmt); + String createTable = "create table " + squareBracketTableName + " (c1 int)"; + stmt.execute(createTable); + + String valid = "insert into " + squareBracketTableName + " values (?)"; + pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); + pstmt.setInt(1, 1); + pstmt.addBatch(); + + pstmt.executeBatch(); + + ResultSet rs = stmt.executeQuery("SELECT * FROM " + squareBracketTableName); + rs.next(); + + assertEquals(rs.getObject(1), 1); + } + + @Test + public void testDoubleQuoteAgainstDB() throws Exception { + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); + f1.setAccessible(true); + f1.set(connection, true); + stmt = (SQLServerStatement) connection.createStatement(); + + Utils.dropTableIfExists(doubleQuoteTableName, stmt); + String createTable = "create table " + doubleQuoteTableName + " (c1 int)"; + stmt.execute(createTable); + + String valid = "insert into " + doubleQuoteTableName + " values (?)"; + pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); + pstmt.setInt(1, 1); + pstmt.addBatch(); + + pstmt.executeBatch(); + + ResultSet rs = stmt.executeQuery("SELECT * FROM " + doubleQuoteTableName); + rs.next(); + + assertEquals(rs.getObject(1), 1); + } + + @Test + public void testSchemaAgainstDB() throws Exception { + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); + f1.setAccessible(true); + f1.set(connection, true); + stmt = (SQLServerStatement) connection.createStatement(); + + Utils.dropTableIfExists("[dbo]." + squareBracketTableName, stmt); + String schemaTableName = "[test] /*some comment*/ . \"dbo\" . /*some comment */ " + squareBracketTableName; + + String createTable = "create table " + schemaTableName + " (c1 int)"; + stmt.execute(createTable); + + String valid = "insert into " + schemaTableName + " values (?)"; + pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); + pstmt.setInt(1, 1); + pstmt.addBatch(); + + pstmt.executeBatch(); + + ResultSet rs = stmt.executeQuery("SELECT * FROM " + schemaTableName); + rs.next(); + + assertEquals(rs.getObject(1), 1); + } + + @Test + public void testColumnNameMixAgainstDB() throws Exception { + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); + f1.setAccessible(true); + f1.set(connection, true); + stmt = (SQLServerStatement) connection.createStatement(); + + Utils.dropTableIfExists(squareBracketTableName, stmt); + String createTable = "create table " + squareBracketTableName + " ([c]]]]1] int, [c]]]]2] int)"; + stmt.execute(createTable); + + String valid = "insert into " + squareBracketTableName + " ([c]]]]1], [c]]]]2]) values (?, 1)"; + pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); + pstmt.setInt(1, 1); + pstmt.addBatch(); + + pstmt.executeBatch(); + + ResultSet rs = stmt.executeQuery("SELECT * FROM " + squareBracketTableName); + rs.next(); + + assertEquals(rs.getObject(1), 1); + } + @BeforeEach public void testSetup() throws TestAbortedException, Exception { connection = DriverManager.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); - SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); + stmt = (SQLServerStatement) connection.createStatement(); Utils.dropTableIfExists(tableName, stmt); String sql1 = "create table " + tableName + " " @@ -343,8 +444,10 @@ public void testSetup() throws TestAbortedException, Exception { public static void terminateVariation() throws SQLException { connection = DriverManager.getConnection(connectionString); - SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); + stmt = (SQLServerStatement) connection.createStatement(); Utils.dropTableIfExists(tableName, stmt); + Utils.dropTableIfExists(squareBracketTableName, stmt); + Utils.dropTableIfExists(doubleQuoteTableName, stmt); if (null != pstmt) { pstmt.close(); From 84acbb8d7e03ada3fad09144d0039e1c9ec57668 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 19 Jun 2018 16:57:48 -0700 Subject: [PATCH 62/84] dont use test database in tests --- .../jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java index 39b04aea1..843056d66 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java @@ -375,7 +375,7 @@ public void testSchemaAgainstDB() throws Exception { stmt = (SQLServerStatement) connection.createStatement(); Utils.dropTableIfExists("[dbo]." + squareBracketTableName, stmt); - String schemaTableName = "[test] /*some comment*/ . \"dbo\" . /*some comment */ " + squareBracketTableName; + String schemaTableName = "\"dbo\" . /*some comment */ " + squareBracketTableName; String createTable = "create table " + schemaTableName + " (c1 int)"; stmt.execute(createTable); From 38bcdc6cd2df060b6e2b65ebe5cb067f18eaf74d Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Wed, 20 Jun 2018 12:17:55 -0700 Subject: [PATCH 63/84] Change exception handling as per JDBC specs --- .../jdbc/SQLServerPreparedStatement.java | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 72d234145..9e02e4d90 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -2481,6 +2481,24 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL try { if (isInsert(localUserSQL) && connection.isAzureDW() && (this.useBulkCopyForBatchInsert)) { + // From the JDBC spec, section 9.1.4 - Making Batch Updates: + // The CallableStatement.executeBatch method (inherited from PreparedStatement) will + // throw a BatchUpdateException if the stored procedure returns anything other than an + // update count or takes OUT or INOUT parameters. + // + // Non-update count results (e.g. ResultSets) are treated as individual batch errors + // when they are encountered in the response. + // + // OUT and INOUT parameter checking is done here, before executing the batch. If any + // OUT or INOUT are present, the entire batch fails. + for (Parameter[] paramValues : batchParamValues) { + for (Parameter paramValue : paramValues) { + if (paramValue.isOutput()) { + throw new BatchUpdateException(SQLServerException.getErrString("R_outParamsNotPermittedinBatch"), null, 0, null); + } + } + } + if (batchParamValues == null) { updateCounts = new int[0]; loggerExternal.exiting(getClassNameLogging(), "executeBatch", updateCounts); @@ -2537,9 +2555,8 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL } } catch (SQLException e) { - // Unable to retrieve metadata for destination - // create an error message for failing bulk copy + insert batch - throw new SQLServerException(SQLServerException.getErrString("R_unableRetrieveColMeta"), e); + // throw a BatchUpdateException with the given error message, and return null for the updateCounts. + throw new BatchUpdateException(e.getMessage(), null, 0, null); } catch (Exception e) { // If we fail with non-SQLException, fall back to the original batch insert logic. From 3cb80b3d0947394b71752d4d7d9bee82cb39e321 Mon Sep 17 00:00:00 2001 From: ulvii Date: Wed, 20 Jun 2018 13:28:00 -0700 Subject: [PATCH 64/84] Add | Add missing license headers (#725) --- .../microsoft/sqlserver/jdbc/ISQLServerConnection43.java | 8 ++++++++ .../microsoft/sqlserver/jdbc/InternalSpatialDatatype.java | 8 ++++++++ .../java/com/microsoft/sqlserver/jdbc/KerbCallback.java | 8 ++++++++ .../microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java | 8 ++++++++ .../microsoft/sqlserver/jdbc/SQLServerConnection43.java | 8 ++++++++ .../com/microsoft/sqlserver/jdbc/SqlFedAuthToken.java | 8 ++++++++ .../java/com/microsoft/sqlserver/jdbc/ThreePartName.java | 8 ++++++++ .../jdbc/connection/RequestBoundaryMethodsTest.java | 8 ++++++++ .../jdbc/ssl/trustmanager/CustomTrustManagerTest.java | 8 ++++++++ .../jdbc/ssl/trustmanager/InvalidTrustManager.java | 8 ++++++++ .../jdbc/ssl/trustmanager/PermissiveTrustManager.java | 8 ++++++++ .../ssl/trustmanager/TrustManagerWithConstructorArg.java | 8 ++++++++ .../sqlserver/testframework/util/ComparisonUtil.java | 8 ++++++++ .../sqlserver/testframework/util/RandomData.java | 8 ++++++++ .../com/microsoft/sqlserver/testframework/util/Util.java | 8 ++++++++ 15 files changed, 120 insertions(+) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java index b8230f5f9..d970ad55e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java @@ -1,3 +1,11 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + package com.microsoft.sqlserver.jdbc; import java.sql.SQLException; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/InternalSpatialDatatype.java b/src/main/java/com/microsoft/sqlserver/jdbc/InternalSpatialDatatype.java index a4fc6edfb..003e30f5d 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/InternalSpatialDatatype.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/InternalSpatialDatatype.java @@ -1,3 +1,11 @@ +/* + * 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; public enum InternalSpatialDatatype { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/KerbCallback.java b/src/main/java/com/microsoft/sqlserver/jdbc/KerbCallback.java index 6f861c4bb..f2e312008 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/KerbCallback.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/KerbCallback.java @@ -1,3 +1,11 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + package com.microsoft.sqlserver.jdbc; import java.io.IOException; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java index 096543ffd..a04b98296 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java @@ -1,3 +1,11 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + package com.microsoft.sqlserver.jdbc; import java.io.IOException; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java index bf1dcd5f2..e48d6c6ac 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java @@ -1,3 +1,11 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + package com.microsoft.sqlserver.jdbc; import java.sql.SQLException; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SqlFedAuthToken.java b/src/main/java/com/microsoft/sqlserver/jdbc/SqlFedAuthToken.java index 0f1287960..99cb4888a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SqlFedAuthToken.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SqlFedAuthToken.java @@ -1,3 +1,11 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + package com.microsoft.sqlserver.jdbc; import java.util.Date; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ThreePartName.java b/src/main/java/com/microsoft/sqlserver/jdbc/ThreePartName.java index 6882de47d..dc6e21be8 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ThreePartName.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ThreePartName.java @@ -1,3 +1,11 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + package com.microsoft.sqlserver.jdbc; import java.util.regex.Matcher; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java index e646f63ed..6f21fab11 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java @@ -1,3 +1,11 @@ +/* + * 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 static org.junit.Assert.assertNotNull; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/CustomTrustManagerTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/CustomTrustManagerTest.java index 67aa770f7..deec89c8f 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/CustomTrustManagerTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/CustomTrustManagerTest.java @@ -1,3 +1,11 @@ +/* + * 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.ssl.trustmanager; import java.sql.DriverManager; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/InvalidTrustManager.java b/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/InvalidTrustManager.java index f54fcf5a1..c657b967e 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/InvalidTrustManager.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/InvalidTrustManager.java @@ -1,3 +1,11 @@ +/* + * 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.ssl.trustmanager; import java.security.cert.CertificateException; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/PermissiveTrustManager.java b/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/PermissiveTrustManager.java index 11c824385..12186ee7e 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/PermissiveTrustManager.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/PermissiveTrustManager.java @@ -1,3 +1,11 @@ +/* + * 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.ssl.trustmanager; import java.security.cert.CertificateException; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/TrustManagerWithConstructorArg.java b/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/TrustManagerWithConstructorArg.java index 4b572fccc..09b5a3a80 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/TrustManagerWithConstructorArg.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/TrustManagerWithConstructorArg.java @@ -1,3 +1,11 @@ +/* + * 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.ssl.trustmanager; import java.io.IOException; diff --git a/src/test/java/com/microsoft/sqlserver/testframework/util/ComparisonUtil.java b/src/test/java/com/microsoft/sqlserver/testframework/util/ComparisonUtil.java index b251e92bd..8de791994 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/util/ComparisonUtil.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/util/ComparisonUtil.java @@ -1,3 +1,11 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + package com.microsoft.sqlserver.testframework.util; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/microsoft/sqlserver/testframework/util/RandomData.java b/src/test/java/com/microsoft/sqlserver/testframework/util/RandomData.java index e32061109..50be438c8 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/util/RandomData.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/util/RandomData.java @@ -1,3 +1,11 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + package com.microsoft.sqlserver.testframework.util; import java.math.BigDecimal; diff --git a/src/test/java/com/microsoft/sqlserver/testframework/util/Util.java b/src/test/java/com/microsoft/sqlserver/testframework/util/Util.java index fc0df86b9..9df214e99 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/util/Util.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/util/Util.java @@ -1,3 +1,11 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + package com.microsoft.sqlserver.testframework.util; import java.sql.CallableStatement; From b4d4b26460e0ea07b1d2d93c1727a50ec6d1c332 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Wed, 20 Jun 2018 13:52:59 -0700 Subject: [PATCH 65/84] remove some comments --- .../sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java index 3d366e5e5..c937c5f29 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java @@ -103,10 +103,6 @@ public boolean isAutoIncrement(int column) { private Object convertValue(ColumnMetadata cm, Object data) throws SQLServerException { switch (cm.columnType) { - /* - * Both BCP and BULK INSERT considers double quotes as part of the data and throws error if any data (say "10") is to be inserted into an - * numeric column. Our implementation does the same. - */ case Types.INTEGER: { // Formatter to remove the decimal part as SQL Server floors the decimal in integer types DecimalFormat decimalFormatter = new DecimalFormat("#"); From 48139e7b9f18b93b98e6f21f7126a58b89513318 Mon Sep 17 00:00:00 2001 From: v-reye Date: Wed, 20 Jun 2018 16:13:57 -0700 Subject: [PATCH 66/84] Enable verify data (#724) Fix to enable data verification in Junit tests. Also addresses intermittent failures with Time/Timestamp where the precision was being inaccurately judged. --- .../sqlserver/testframework/DBResultSet.java | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/testframework/DBResultSet.java b/src/test/java/com/microsoft/sqlserver/testframework/DBResultSet.java index d281effca..f9bd04e91 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/DBResultSet.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/DBResultSet.java @@ -17,10 +17,7 @@ import java.sql.JDBCType; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Time; import java.sql.Timestamp; -import java.time.LocalDateTime; -import java.time.LocalTime; import java.util.Arrays; import java.util.Calendar; import java.util.logging.Level; @@ -212,8 +209,7 @@ public void verifydata(int ordinal, Object retrieved = this.getXXX(ordinal + 1, coercion); // Verify - // TODO: Check the intermittent verification error - // verifydata(ordinal, coercion, expectedData, retrieved); + verifydata(ordinal, coercion, expectedData, retrieved); } /** @@ -268,7 +264,10 @@ public void verifydata(int ordinal, break; case java.sql.Types.REAL: - assertTrue((((Float) expectedData).floatValue() == ((Float) retrieved).floatValue()), + if (expectedData instanceof Double) { + expectedData = (Float) (((Double) expectedData).floatValue()); + } + assertTrue(((Float) expectedData).floatValue() == ((Float) retrieved).floatValue(), "Unexpected real value, expected: " + (Float) expectedData + " received: " + (Float) retrieved); break; @@ -297,10 +296,14 @@ else if (metaData.getColumnTypeName(ordinal + 1).equalsIgnoreCase("smalldatetime + " ,received: " + (((Timestamp) retrieved).getTime())); break; } - else - assertTrue(("" + Timestamp.valueOf((LocalDateTime) expectedData)).equalsIgnoreCase("" + retrieved), "Unexpected datetime2 value, " - + "expected: " + Timestamp.valueOf((LocalDateTime) expectedData) + " ,received: " + retrieved); - break; + else { + String retrievedTimestamp = retrieved.toString(); + String expectedTimestamp = expectedData.toString().substring(0,retrievedTimestamp.length()); + assertTrue(expectedTimestamp.equalsIgnoreCase(retrievedTimestamp), "Unexpected datetime2 value, " + "expected: " + + expectedTimestamp + " ,received: " + retrievedTimestamp); + break; + } + case java.sql.Types.DATE: assertTrue((("" + expectedData).equalsIgnoreCase("" + retrieved)), @@ -308,8 +311,10 @@ else if (metaData.getColumnTypeName(ordinal + 1).equalsIgnoreCase("smalldatetime break; case java.sql.Types.TIME: - assertTrue(("" + Time.valueOf((LocalTime) expectedData)).equalsIgnoreCase("" + retrieved), - "Unexpected time value, exptected: " + Time.valueOf((LocalTime) expectedData) + " ,received: " + retrieved); + String retrievedTime = retrieved.toString(); + String expectedTime = expectedData.toString().substring(0,retrievedTime.length()); + assertTrue(expectedTime.equalsIgnoreCase(retrievedTime), + "Unexpected time value, expected: " + expectedTime + " ,received: " + retrievedTime); break; case microsoft.sql.Types.DATETIMEOFFSET: @@ -446,7 +451,7 @@ private static Object roundSmallDateTimeValue(Object value) { cal = (Calendar) value; } else { - ts = (java.sql.Timestamp) value; + ts = Timestamp.valueOf((String) value); cal = Calendar.getInstance(); cal.setTimeInMillis(ts.getTime()); nanos = ts.getNanos(); @@ -546,4 +551,4 @@ public DBStatement statement() { return (null); } -} \ No newline at end of file +} From 61b78d7bb329d919fbe58b17accf0e31bebb26e3 Mon Sep 17 00:00:00 2001 From: Sehrope Sarkuni Date: Wed, 20 Jun 2018 20:54:28 -0400 Subject: [PATCH 67/84] Fix | Refactored socket creation to simplify handling of socket creation Refactors socket creation in SocketFinder.findSocket(...) to simplify handling of socket creation. When the host resolves to a single address the driver now defers to getConnectedSocket(...) to create the socket without spawning any threads. This happens regardless of whether we're running on an IBM JDK. Previously the single address case would still use NIO on an IBM JDK. On non-IBM JDKs the driver now handles both IPv4 and IPv6 addresses concurrently with a single shared timeout. Previously hosts that resolved to both types of addresses were allowed half the timeout for socket creation per address type with the resolution performed sequentially. --- .../microsoft/sqlserver/jdbc/IOBuffer.java | 58 ++++--------------- 1 file changed, 11 insertions(+), 47 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 5d9e1652e..8aa74ea7d 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -2329,58 +2329,22 @@ else if (!useTnir) { conn.terminate(SQLServerException.DRIVER_ERROR_UNSUPPORTED_CONFIG, errorStr); } + if (inetAddrs.length == 1) { + // Single address so do not start any threads + return getConnectedSocket(inetAddrs[0], portNumber, timeoutInMilliSeconds); + } + timeoutInMilliSeconds = Math.max(timeoutInMilliSeconds, minTimeoutForParallelConnections); if (Util.isIBM()) { - timeoutInMilliSeconds = Math.max(timeoutInMilliSeconds, minTimeoutForParallelConnections); if (logger.isLoggable(Level.FINER)) { logger.finer(this.toString() + "Using Java NIO with timeout:" + timeoutInMilliSeconds); } findSocketUsingJavaNIO(inetAddrs, portNumber, timeoutInMilliSeconds); } else { - LinkedList inet4Addrs = new LinkedList<>(); - LinkedList inet6Addrs = new LinkedList<>(); - - for (InetAddress inetAddr : inetAddrs) { - if (inetAddr instanceof Inet4Address) { - inet4Addrs.add((Inet4Address) inetAddr); - } - else { - assert inetAddr instanceof Inet6Address : "Unexpected IP address " + inetAddr.toString(); - inet6Addrs.add((Inet6Address) inetAddr); - } - } - - // use half timeout only if both IPv4 and IPv6 addresses are present - int timeoutForEachIPAddressType; - if ((!inet4Addrs.isEmpty()) && (!inet6Addrs.isEmpty())) { - timeoutForEachIPAddressType = Math.max(timeoutInMilliSeconds / 2, minTimeoutForParallelConnections); - } - else - timeoutForEachIPAddressType = Math.max(timeoutInMilliSeconds, minTimeoutForParallelConnections); - - if (!inet4Addrs.isEmpty()) { - if (logger.isLoggable(Level.FINER)) { - logger.finer(this.toString() + "Using Java Threading with timeout:" + timeoutForEachIPAddressType); - } - - findSocketUsingThreading(inet4Addrs, portNumber, timeoutForEachIPAddressType); - } - - if (!result.equals(Result.SUCCESS)) { - // try threading logic - if (!inet6Addrs.isEmpty()) { - // do not start any threads if there is only one ipv6 address - if (inet6Addrs.size() == 1) { - return getConnectedSocket(inet6Addrs.get(0), portNumber, timeoutForEachIPAddressType); - } - - if (logger.isLoggable(Level.FINER)) { - logger.finer(this.toString() + "Using Threading with timeout:" + timeoutForEachIPAddressType); - } - - findSocketUsingThreading(inet6Addrs, portNumber, timeoutForEachIPAddressType); - } + if (logger.isLoggable(Level.FINER)) { + logger.finer(this.toString() + "Using Threading with timeout:" + timeoutInMilliSeconds); } + findSocketUsingThreading(inetAddrs, portNumber, timeoutInMilliSeconds); } // If the thread continued execution due to timeout, the result may not be known. @@ -2633,12 +2597,12 @@ private Socket getConnectedSocket(InetSocketAddress addr, return selectedSocket; } - private void findSocketUsingThreading(LinkedList inetAddrs, + private void findSocketUsingThreading(InetAddress[] inetAddrs, int portNumber, int timeoutInMilliSeconds) throws IOException, InterruptedException { assert timeoutInMilliSeconds != 0 : "The timeout cannot be zero"; - assert inetAddrs.isEmpty() == false : "Number of inetAddresses should not be zero in this function"; + assert inetAddrs.length != 0 : "Number of inetAddresses should not be zero in this function"; LinkedList sockets = new LinkedList<>(); LinkedList socketConnectors = new LinkedList<>(); @@ -2646,7 +2610,7 @@ private void findSocketUsingThreading(LinkedList inetAddrs, try { // create a socket, inetSocketAddress and a corresponding socketConnector per inetAddress - noOfSpawnedThreads = inetAddrs.size(); + noOfSpawnedThreads = inetAddrs.length; for (InetAddress inetAddress : inetAddrs) { Socket s = new Socket(); sockets.add(s); From 9b7f40c429ba03bfea2baf13bb6b6aa96540fd5a Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Fri, 22 Jun 2018 03:22:20 -0700 Subject: [PATCH 68/84] reflect comments --- .../sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java | 4 ++-- .../microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java | 4 ++-- .../com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java index c937c5f29..964a0fbee 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java @@ -171,7 +171,7 @@ private Object convertValue(ColumnMetadata cm, } } - case 2013: // java.sql.Types.TIME_WITH_TIMEZONE + case java.sql.Types.TIME_WITH_TIMEZONE: { OffsetTime offsetTimeValue; @@ -186,7 +186,7 @@ else if (timeFormatter != null) return offsetTimeValue; } - case 2014: // java.sql.Types.TIMESTAMP_WITH_TIMEZONE + case java.sql.Types.TIMESTAMP_WITH_TIMEZONE: { OffsetDateTime offsetDateTimeValue; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java index e89e36a78..7ed34b62d 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java @@ -390,7 +390,7 @@ public Object[] getRowData() throws SQLServerException { break; } - case 2013: // java.sql.Types.TIME_WITH_TIMEZONE + case java.sql.Types.TIME_WITH_TIMEZONE: { OffsetTime offsetTimeValue; @@ -406,7 +406,7 @@ else if (timeFormatter != null) break; } - case 2014: // java.sql.Types.TIMESTAMP_WITH_TIMEZONE + case java.sql.Types.TIMESTAMP_WITH_TIMEZONE: { OffsetDateTime offsetDateTimeValue; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java index cc14d2c6a..206cd9ef0 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java @@ -156,10 +156,10 @@ void addColumnMetadataInternal(int positionInSource, if (null != name) colName = name.trim(); - else if ((columnNames != null) && (columnNames.length >= positionInSource)) + else if ((null != columnNames) && (columnNames.length >= positionInSource)) colName = columnNames[positionInSource - 1]; - if ((columnNames != null) && (positionInSource > columnNames.length)) { + if ((null != columnNames) && (positionInSource > columnNames.length)) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumn")); Object[] msgArgs = {positionInSource}; throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); From abc985140b182171eab746bdaeae9e15960d0f51 Mon Sep 17 00:00:00 2001 From: ulvii Date: Fri, 22 Jun 2018 15:11:02 -0700 Subject: [PATCH 69/84] Add support for UTF-8 feature extension. (#722) * Add | Support for UTF8 changes --- .../microsoft/sqlserver/jdbc/IOBuffer.java | 4 + .../sqlserver/jdbc/SQLCollation.java | 11 +- .../sqlserver/jdbc/SQLServerConnection.java | 26 +++ .../sqlserver/jdbc/SQLServerResource.java | 3 +- .../sqlserver/jdbc/unit/UTF8SupportTest.java | 174 ++++++++++++++++++ .../sqlserver/testframework/Utils.java | 11 +- 6 files changed, 225 insertions(+), 4 deletions(-) create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/unit/UTF8SupportTest.java diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 8aa74ea7d..d1aa53d7a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -118,6 +118,8 @@ final class TDS { static final int AES_256_CBC = 1; static final int AEAD_AES_256_CBC_HMAC_SHA256 = 2; static final int AE_METADATA = 0x08; + + static final byte TDS_FEATURE_EXT_UTF8SUPPORT = 0x0A; static final int TDS_TVP = 0xF3; static final int TVP_ROW = 0x01; @@ -177,6 +179,8 @@ static final String getTokenName(int tdsTokenType) { return "TDS_DONEINPROC (0xFF)"; case TDS_FEDAUTHINFO: return "TDS_FEDAUTHINFO (0xEE)"; + case TDS_FEATURE_EXT_UTF8SUPPORT: + return "TDS_FEATURE_EXT_UTF8SUPPORT (0x0A)"; default: return "unknown token (0x" + Integer.toHexString(tdsTokenType).toUpperCase() + ")"; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java index d89a95e11..1a0f02b51 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java @@ -39,6 +39,7 @@ final class SQLCollation implements java.io.Serializable private int langID() { return info & 0x0000FFFF; } private final int sortId; // 5th byte of TDS collation. private final Encoding encoding; + private static final int UTF8_IN_TDSCOLLATION = 0x4000000; // Utility methods for getting details of this collation's encoding final Charset getCharset() throws SQLServerException { return encoding.charset(); } @@ -77,8 +78,13 @@ int getCollationSortID() { */ info = tdsReader.readInt(); // 4 bytes, contains: LCID ColFlags Version sortId = tdsReader.readUnsignedByte(); // 1 byte, contains: SortId - // For a SortId==0 collation, the LCID bits correspond to a LocaleId - encoding = (0 == sortId) ? encodingFromLCID() : encodingFromSortId(); + if (UTF8_IN_TDSCOLLATION == (info & UTF8_IN_TDSCOLLATION)) { + encoding = Encoding.UTF8; + } + else { + // For a SortId==0 collation, the LCID bits correspond to a LocaleId + encoding = (0 == sortId) ? encodingFromLCID() : encodingFromSortId(); + } } /** @@ -549,6 +555,7 @@ private Encoding encodingFromSortId() throws UnsupportedEncodingException { enum Encoding { UNICODE ("UTF-16LE", true, false), + UTF8 ("UTF-8", true, false), CP437 ("Cp437", false, false), CP850 ("Cp850", false, false), CP874 ("MS874", true, true), diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 918d58ee4..25debf539 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -3432,6 +3432,16 @@ int writeFedAuthFeatureRequest(boolean write, return totalLen; } + int writeUTF8SupportFeatureRequest(boolean write, + TDSWriter tdsWriter /* if false just calculates the length */) throws SQLServerException { + int len = 5; // 1byte = featureID, 4bytes = featureData length + if (write) { + tdsWriter.writeByte(TDS.TDS_FEATURE_EXT_UTF8SUPPORT); + tdsWriter.writeInt(0); + } + return len; + } + private final class LogonCommand extends UninterruptableTDSCommand { LogonCommand() { super("logon"); @@ -4130,7 +4140,19 @@ private void onFeatureExtAck(int featureId, serverSupportsColumnEncryption = true; break; } + case TDS.TDS_FEATURE_EXT_UTF8SUPPORT: { + if (connectionlogger.isLoggable(Level.FINER)) { + connectionlogger.fine(toString() + " Received feature extension acknowledgement for UTF8 support."); + } + if (1 > data.length) { + if (connectionlogger.isLoggable(Level.SEVERE)) { + connectionlogger.severe(toString() + " Unknown value for UTF8 support."); + } + throw new SQLServerException(SQLServerException.getErrString("R_unknownUTF8SupportValue"), null); + } + break; + } default: { // Unknown feature ack if (connectionlogger.isLoggable(Level.SEVERE)) { @@ -4419,6 +4441,8 @@ else if (serverMajorVersion >= 9) // Yukon (9.0) --> TDS 7.2 // Prelogin disconn len2 = len2 + 1; // add 1 to length becaue of FeatureEx terminator + len2 = len2 + writeUTF8SupportFeatureRequest(false, tdsWriter); + // Length of entire Login 7 packet tdsWriter.writeInt(len2); tdsWriter.writeInt(tdsVersion); @@ -4598,6 +4622,8 @@ else if (serverMajorVersion >= 9) // Yukon (9.0) --> TDS 7.2 // Prelogin disconn writeFedAuthFeatureRequest(true, tdsWriter, fedAuthFeatureExtensionData); } + writeUTF8SupportFeatureRequest(true, tdsWriter); + tdsWriter.writeByte((byte) TDS.FEATURE_EXT_TERMINATOR); tdsWriter.setDataLoggable(true); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 3cea768e6..483d00c4c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -393,8 +393,9 @@ protected Object[][] getContents() { {"R_invalidSSLProtocol", "SSL Protocol {0} label is not valid. Only TLS, TLSv1, TLSv1.1, and TLSv1.2 are supported."}, {"R_cancelQueryTimeoutPropertyDescription", "The number of seconds to wait to cancel sending a query timeout."}, {"R_invalidCancelQueryTimeout", "The cancel timeout value {0} is not valid."}, + {"R_unknownUTF8SupportValue", "Unknown value for UTF8 support."}, {"R_illegalWKT", "Illegal Well-Known text. Please make sure Well-Known text is valid."}, {"R_illegalTypeForGeometry", "{0} is not supported for Geometry."}, {"R_illegalWKTposition", "Illegal character in Well-Known text at position {0}."}, }; -} \ No newline at end of file +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/UTF8SupportTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/UTF8SupportTest.java new file mode 100644 index 000000000..68ee66dda --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/UTF8SupportTest.java @@ -0,0 +1,174 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + +package com.microsoft.sqlserver.jdbc.unit; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.nio.charset.StandardCharsets; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Collections; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; +import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.PrepUtil; +import com.microsoft.sqlserver.testframework.Utils; +import com.microsoft.sqlserver.testframework.util.RandomUtil; + +/** + * A class for testing the UTF8 support changes. + */ +@RunWith(JUnitPlatform.class) +public class UTF8SupportTest extends AbstractTest { + private static Connection connection; + private static String databaseName; + private static String tableName; + + /** + * Test against UTF8 CHAR type. + * + * @throws SQLException + */ + @Test + public void testChar() throws SQLException { + if (Utils.serverSupportsUTF8(connection)) { + createTable("char(10)"); + validate("teststring"); + // This is 10 UTF-8 bytes. D1 82 D0 B5 D1 81 D1 82 31 32 + validate("тест12"); + // E2 95 A1 E2 95 A4 E2 88 9E 2D + validate("╡╤∞-"); + + createTable("char(4000)"); + validate(String.join("", Collections.nCopies(400, "teststring"))); + validate(String.join("", Collections.nCopies(400, "тест12"))); + validate(String.join("", Collections.nCopies(400, "╡╤∞-"))); + + createTable("char(4001)"); + validate(String.join("", Collections.nCopies(400, "teststring")) + "1"); + validate(String.join("", Collections.nCopies(400, "тест12")) + "1"); + validate(String.join("", Collections.nCopies(400, "╡╤∞-")) + "1"); + + createTable("char(8000)"); + validate(String.join("", Collections.nCopies(800, "teststring"))); + validate(String.join("", Collections.nCopies(800, "тест12"))); + validate(String.join("", Collections.nCopies(800, "╡╤∞-"))); + } + } + + /** + * Test against UTF8 VARCHAR type. + * + * @throws SQLException + */ + @Test + public void testVarchar() throws SQLException { + if (Utils.serverSupportsUTF8(connection)) { + createTable("varchar(10)"); + validate("teststring"); + validate("тест12"); + validate("╡╤∞-"); + + createTable("varchar(4000)"); + validate(String.join("", Collections.nCopies(400, "teststring"))); + validate(String.join("", Collections.nCopies(400, "тест12"))); + validate(String.join("", Collections.nCopies(400, "╡╤∞-"))); + + createTable("varchar(4001)"); + validate(String.join("", Collections.nCopies(400, "teststring")) + "1"); + validate(String.join("", Collections.nCopies(400, "тест12")) + "1"); + validate(String.join("", Collections.nCopies(400, "╡╤∞-")) + "1"); + + createTable("varchar(8000)"); + validate(String.join("", Collections.nCopies(800, "teststring"))); + validate(String.join("", Collections.nCopies(800, "тест12"))); + validate(String.join("", Collections.nCopies(800, "╡╤∞-"))); + + createTable("varchar(MAX)"); + validate(String.join("", Collections.nCopies(800, "teststring"))); + validate(String.join("", Collections.nCopies(800, "тест12"))); + validate(String.join("", Collections.nCopies(800, "╡╤∞-"))); + } + } + + @BeforeAll + public static void setUp() throws ClassNotFoundException, SQLException { + connection = PrepUtil.getConnection(getConfiguredProperty("mssql_jdbc_test_connection_properties")); + if (Utils.serverSupportsUTF8(connection)) { + databaseName = RandomUtil.getIdentifier("UTF8Database"); + tableName = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("RequestBoundaryTable")); + createDatabaseWithUTF8Collation(); + connection.setCatalog(databaseName); + } + } + + @AfterAll + public static void cleanUp() throws SQLException { + if (Utils.serverSupportsUTF8(connection)) { + Utils.dropDatabaseIfExists(databaseName, connection.createStatement()); + } + connection.close(); + } + + private static void createDatabaseWithUTF8Collation() throws SQLException { + try (Statement stmt = connection.createStatement();) { + stmt.executeUpdate("CREATE DATABASE " + AbstractSQLGenerator.escapeIdentifier(databaseName) + " COLLATE Cyrillic_General_100_CS_AS_UTF8"); + } + } + + private static void createTable(String columnType) throws SQLException { + try (Statement stmt = connection.createStatement();) { + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("CREATE TABLE " + tableName + " (c " + columnType + ")"); + } + } + + public void clearTable() throws SQLException { + try (Statement stmt = connection.createStatement();) { + stmt.executeUpdate("DELETE FROM " + tableName); + } + } + + public void validate(String value) throws SQLException { + try (PreparedStatement psInsert = connection.prepareStatement("INSERT INTO " + tableName + " VALUES(?)"); + PreparedStatement psFetch = connection.prepareStatement("SELECT * FROM " + tableName); + Statement stmt = connection.createStatement();) { + clearTable(); + // Used for exact byte comparison. + byte[] valueBytes = value.getBytes(StandardCharsets.UTF_8); + + psInsert.setString(1, value); + psInsert.executeUpdate(); + + // Fetch using Statement. + ResultSet rsStatement = stmt.executeQuery("SELECT * FROM " + tableName); + rsStatement.next(); + // Compare Strings. + assertEquals(value, rsStatement.getString(1)); + // Test UTF8 sequence returned from getBytes(). + assertArrayEquals(valueBytes, rsStatement.getBytes(1)); + + // Fetch using PreparedStatement. + ResultSet rsPreparedStatement = psFetch.executeQuery(); + rsPreparedStatement.next(); + assertEquals(value, rsPreparedStatement.getString(1)); + assertArrayEquals(valueBytes, rsPreparedStatement.getBytes(1)); + } + } +} diff --git a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java index cf761bbb1..56edd3f58 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java @@ -15,7 +15,9 @@ import java.io.CharArrayReader; import java.net.URI; import java.sql.Connection; +import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; import java.util.ArrayList; import java.util.Arrays; import java.util.logging.Level; @@ -286,7 +288,7 @@ public static void dropProcedureIfExists(String procName, java.sql.Statement stm public static void dropDatabaseIfExists(String databaseName, java.sql.Statement stmt) throws SQLException { - stmt.executeUpdate("IF EXISTS(SELECT * from sys.databases WHERE name='" + databaseName + "') DROP DATABASE [" + databaseName + "]"); + stmt.executeUpdate("USE MASTER; IF EXISTS(SELECT * from sys.databases WHERE name='" + databaseName + "') DROP DATABASE [" + databaseName + "]"); } /** @@ -328,4 +330,11 @@ public static boolean isJDBC43OrGreater(Connection connection) throws SQLExcepti public static float getJDBCVersion(Connection connection) throws SQLException { return Float.valueOf(connection.getMetaData().getJDBCMajorVersion() + "." + connection.getMetaData().getJDBCMinorVersion()); } + + public static boolean serverSupportsUTF8(Connection connection) throws SQLException { + try (Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT name FROM sys.fn_helpcollations() WHERE name LIKE '%UTF8%'");) { + return rs.isBeforeFirst(); + } + } } From 405b47e776897bcf22a8470c6fc45bc233786517 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Fri, 22 Jun 2018 15:30:53 -0700 Subject: [PATCH 70/84] changed how logger works, refactored code in SQLServerBulkCommon due to that, changed exception being thrown to BatchUpdateException, added same logic for parsing in executeLargeBatch, and added tests accordingly. --- .../jdbc/SQLServerBulkBatchInsertRecord.java | 128 ++++++++++++++++-- .../jdbc/SQLServerBulkCSVFileRecord.java | 119 +++++++++++++++- .../sqlserver/jdbc/SQLServerBulkCommon.java | 92 ------------- .../jdbc/SQLServerPreparedStatement.java | 95 ++++++++++++- .../BatchExecutionWithBulkCopyTest.java | 59 ++++++++ 5 files changed, 383 insertions(+), 110 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java index 964a0fbee..8df463d63 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java @@ -20,25 +20,36 @@ import java.util.HashMap; import java.util.List; import java.util.Map.Entry; + +import com.microsoft.sqlserver.jdbc.SQLServerBulkCommon.ColumnMetadata; + import java.util.Set; /** * A simple implementation of the ISQLServerBulkRecord interface that can be used to read in the basic Java data types from an ArrayList of Parameters * that were provided by pstmt/cstmt. */ -public class SQLServerBulkBatchInsertRecord extends SQLServerBulkCommon implements ISQLServerBulkRecord, java.lang.AutoCloseable { +public class SQLServerBulkBatchInsertRecord extends SQLServerBulkCommon implements ISQLServerBulkRecord { private List batchParam; private int batchParamIndex = -1; private List columnList; private List valueList; + + /* + * Class name for logging. + */ + private static final String loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkBatchInsertRecord"; + + /* + * Logger + */ + private static final java.util.logging.Logger loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); public SQLServerBulkBatchInsertRecord(ArrayList batchParam, ArrayList columnList, ArrayList valueList, String encoding) throws SQLServerException { - loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkBatchInsertRecord"; - loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); loggerExternal.entering(loggerClassName, "SQLServerBulkBatchInsertRecord", new Object[] {batchParam, encoding}); if (null == batchParam) { @@ -57,15 +68,6 @@ public SQLServerBulkBatchInsertRecord(ArrayList batchParam, loggerExternal.exiting(loggerClassName, "SQLServerBulkBatchInsertRecord"); } - /** - * Releases any resources associated with the batch. - * - * @throws SQLServerException - * when an error occurs - */ - public void close() throws SQLServerException { - } - public DateTimeFormatter getColumnDateTimeFormatter(int column) { return columnMetadata.get(column).dateTimeFormatter; } @@ -310,6 +312,108 @@ else if (valueData.equalsIgnoreCase("null")) { } return data; } + + @Override + void addColumnMetadataInternal(int positionInSource, + String name, + int jdbcType, + int precision, + int scale, + DateTimeFormatter dateTimeFormatter) throws SQLServerException { + loggerExternal.entering(loggerClassName, "addColumnMetadata", new Object[] {positionInSource, name, jdbcType, precision, scale}); + + String colName = ""; + + if (0 >= positionInSource) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumnOrdinal")); + Object[] msgArgs = {positionInSource}; + throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); + } + + if (null != name) + colName = name.trim(); + else if ((null != columnNames) && (columnNames.length >= positionInSource)) + colName = columnNames[positionInSource - 1]; + + if ((null != columnNames) && (positionInSource > columnNames.length)) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumn")); + Object[] msgArgs = {positionInSource}; + throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); + } + + checkDuplicateColumnName(positionInSource, name); + switch (jdbcType) { + /* + * SQL Server supports numerous string literal formats for temporal types, hence sending them as varchar with approximate + * precision(length) needed to send supported string literals. string literal formats supported by temporal types are available in MSDN + * page on data types. + */ + case java.sql.Types.DATE: + case java.sql.Types.TIME: + case java.sql.Types.TIMESTAMP: + case microsoft.sql.Types.DATETIMEOFFSET: + columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, precision, scale, dateTimeFormatter)); + break; + + // Redirect SQLXML as LONGNVARCHAR + // SQLXML is not valid type in TDS + case java.sql.Types.SQLXML: + columnMetadata.put(positionInSource, new ColumnMetadata(colName, java.sql.Types.LONGNVARCHAR, precision, scale, dateTimeFormatter)); + break; + + // Redirecting Float as Double based on data type mapping + // https://msdn.microsoft.com/en-us/library/ms378878%28v=sql.110%29.aspx + case java.sql.Types.FLOAT: + columnMetadata.put(positionInSource, new ColumnMetadata(colName, java.sql.Types.DOUBLE, precision, scale, dateTimeFormatter)); + break; + + // redirecting BOOLEAN as BIT + case java.sql.Types.BOOLEAN: + columnMetadata.put(positionInSource, new ColumnMetadata(colName, java.sql.Types.BIT, precision, scale, dateTimeFormatter)); + break; + + default: + columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, precision, scale, dateTimeFormatter)); + } + + loggerExternal.exiting(loggerClassName, "addColumnMetadata"); + } + + @Override + public void setTimestampWithTimezoneFormat(String dateTimeFormat) { + loggerExternal.entering(loggerClassName, "setTimestampWithTimezoneFormat", dateTimeFormat); + + this.dateTimeFormatter = DateTimeFormatter.ofPattern(dateTimeFormat); + + loggerExternal.exiting(loggerClassName, "setTimestampWithTimezoneFormat"); + } + + @Override + public void setTimestampWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { + loggerExternal.entering(loggerClassName, "setTimestampWithTimezoneFormat", new Object[] {dateTimeFormatter}); + + this.dateTimeFormatter = dateTimeFormatter; + + loggerExternal.exiting(loggerClassName, "setTimestampWithTimezoneFormat"); + } + + @Override + public void setTimeWithTimezoneFormat(String timeFormat) { + loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", timeFormat); + + this.timeFormatter = DateTimeFormatter.ofPattern(timeFormat); + + loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); + } + + @Override + public void setTimeWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { + loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", new Object[] {dateTimeFormatter}); + + this.timeFormatter = dateTimeFormatter; + + loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); + } @Override public boolean next() throws SQLServerException { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java index 7ed34b62d..903a5f653 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java @@ -24,6 +24,9 @@ import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.Map.Entry; + +import com.microsoft.sqlserver.jdbc.SQLServerBulkCommon.ColumnMetadata; + import java.util.Set; /** @@ -47,6 +50,16 @@ public class SQLServerBulkCSVFileRecord extends SQLServerBulkCommon implements I * Delimiter to parse lines with. */ private final String delimiter; + + /* + * Class name for logging. + */ + private static final String loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkCSVFileRecord"; + + /* + * Logger + */ + private static final java.util.logging.Logger loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); /** * Creates a simple reader to parse data from a delimited file with the given encoding. @@ -66,8 +79,6 @@ public SQLServerBulkCSVFileRecord(String fileToParse, String encoding, String delimiter, boolean firstLineIsColumnNames) throws SQLServerException { - loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkCSVFileRecord"; - loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); loggerExternal.entering(loggerClassName, "SQLServerBulkCSVFileRecord", new Object[] {fileToParse, encoding, delimiter, firstLineIsColumnNames}); @@ -128,8 +139,6 @@ public SQLServerBulkCSVFileRecord(InputStream fileToParse, String encoding, String delimiter, boolean firstLineIsColumnNames) throws SQLServerException { - loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkCSVFileRecord"; - loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); loggerExternal.entering(loggerClassName, "SQLServerBulkCSVFileRecord", new Object[] {fileToParse, encoding, delimiter, firstLineIsColumnNames}); @@ -465,6 +474,108 @@ else if (dateTimeFormatter != null) return dataRow; } } + + @Override + void addColumnMetadataInternal(int positionInSource, + String name, + int jdbcType, + int precision, + int scale, + DateTimeFormatter dateTimeFormatter) throws SQLServerException { + loggerExternal.entering(loggerClassName, "addColumnMetadata", new Object[] {positionInSource, name, jdbcType, precision, scale}); + + String colName = ""; + + if (0 >= positionInSource) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumnOrdinal")); + Object[] msgArgs = {positionInSource}; + throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); + } + + if (null != name) + colName = name.trim(); + else if ((null != columnNames) && (columnNames.length >= positionInSource)) + colName = columnNames[positionInSource - 1]; + + if ((null != columnNames) && (positionInSource > columnNames.length)) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumn")); + Object[] msgArgs = {positionInSource}; + throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); + } + + checkDuplicateColumnName(positionInSource, name); + switch (jdbcType) { + /* + * SQL Server supports numerous string literal formats for temporal types, hence sending them as varchar with approximate + * precision(length) needed to send supported string literals. string literal formats supported by temporal types are available in MSDN + * page on data types. + */ + case java.sql.Types.DATE: + case java.sql.Types.TIME: + case java.sql.Types.TIMESTAMP: + case microsoft.sql.Types.DATETIMEOFFSET: + columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, 50, scale, dateTimeFormatter)); + break; + + // Redirect SQLXML as LONGNVARCHAR + // SQLXML is not valid type in TDS + case java.sql.Types.SQLXML: + columnMetadata.put(positionInSource, new ColumnMetadata(colName, java.sql.Types.LONGNVARCHAR, precision, scale, dateTimeFormatter)); + break; + + // Redirecting Float as Double based on data type mapping + // https://msdn.microsoft.com/en-us/library/ms378878%28v=sql.110%29.aspx + case java.sql.Types.FLOAT: + columnMetadata.put(positionInSource, new ColumnMetadata(colName, java.sql.Types.DOUBLE, precision, scale, dateTimeFormatter)); + break; + + // redirecting BOOLEAN as BIT + case java.sql.Types.BOOLEAN: + columnMetadata.put(positionInSource, new ColumnMetadata(colName, java.sql.Types.BIT, precision, scale, dateTimeFormatter)); + break; + + default: + columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, precision, scale, dateTimeFormatter)); + } + + loggerExternal.exiting(loggerClassName, "addColumnMetadata"); + } + + @Override + public void setTimestampWithTimezoneFormat(String dateTimeFormat) { + loggerExternal.entering(loggerClassName, "setTimestampWithTimezoneFormat", dateTimeFormat); + + this.dateTimeFormatter = DateTimeFormatter.ofPattern(dateTimeFormat); + + loggerExternal.exiting(loggerClassName, "setTimestampWithTimezoneFormat"); + } + + @Override + public void setTimestampWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { + loggerExternal.entering(loggerClassName, "setTimestampWithTimezoneFormat", new Object[] {dateTimeFormatter}); + + this.dateTimeFormatter = dateTimeFormatter; + + loggerExternal.exiting(loggerClassName, "setTimestampWithTimezoneFormat"); + } + + @Override + public void setTimeWithTimezoneFormat(String timeFormat) { + loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", timeFormat); + + this.timeFormatter = DateTimeFormatter.ofPattern(timeFormat); + + loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); + } + + @Override + public void setTimeWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { + loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", new Object[] {dateTimeFormatter}); + + this.timeFormatter = dateTimeFormatter; + + loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); + } @Override public boolean next() throws SQLServerException { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java index 206cd9ef0..0d45af6a3 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java @@ -38,16 +38,6 @@ protected class ColumnMetadata { } } - /* - * Class name for logging. - */ - protected static String loggerClassName; - - /* - * Logger - */ - protected java.util.logging.Logger loggerExternal; - /* * Contains all the column names if firstLineIsColumnNames is true */ @@ -144,68 +134,6 @@ void addColumnMetadataInternal(int positionInSource, int precision, int scale, DateTimeFormatter dateTimeFormatter) throws SQLServerException { - loggerExternal.entering(loggerClassName, "addColumnMetadata", new Object[] {positionInSource, name, jdbcType, precision, scale}); - - String colName = ""; - - if (0 >= positionInSource) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumnOrdinal")); - Object[] msgArgs = {positionInSource}; - throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); - } - - if (null != name) - colName = name.trim(); - else if ((null != columnNames) && (columnNames.length >= positionInSource)) - colName = columnNames[positionInSource - 1]; - - if ((null != columnNames) && (positionInSource > columnNames.length)) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumn")); - Object[] msgArgs = {positionInSource}; - throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); - } - - checkDuplicateColumnName(positionInSource, name); - switch (jdbcType) { - /* - * SQL Server supports numerous string literal formats for temporal types, hence sending them as varchar with approximate - * precision(length) needed to send supported string literals. string literal formats supported by temporal types are available in MSDN - * page on data types. - */ - case java.sql.Types.DATE: - case java.sql.Types.TIME: - case java.sql.Types.TIMESTAMP: - case microsoft.sql.Types.DATETIMEOFFSET: - if (this instanceof SQLServerBulkCSVFileRecord) { - columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, 50, scale, dateTimeFormatter)); - } - else { - columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, precision, scale, dateTimeFormatter)); - } - break; - - // Redirect SQLXML as LONGNVARCHAR - // SQLXML is not valid type in TDS - case java.sql.Types.SQLXML: - columnMetadata.put(positionInSource, new ColumnMetadata(colName, java.sql.Types.LONGNVARCHAR, precision, scale, dateTimeFormatter)); - break; - - // Redirecting Float as Double based on data type mapping - // https://msdn.microsoft.com/en-us/library/ms378878%28v=sql.110%29.aspx - case java.sql.Types.FLOAT: - columnMetadata.put(positionInSource, new ColumnMetadata(colName, java.sql.Types.DOUBLE, precision, scale, dateTimeFormatter)); - break; - - // redirecting BOOLEAN as BIT - case java.sql.Types.BOOLEAN: - columnMetadata.put(positionInSource, new ColumnMetadata(colName, java.sql.Types.BIT, precision, scale, dateTimeFormatter)); - break; - - default: - columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, precision, scale, dateTimeFormatter)); - } - - loggerExternal.exiting(loggerClassName, "addColumnMetadata"); } /** @@ -215,11 +143,6 @@ else if ((null != columnNames) && (columnNames.length >= positionInSource)) * format to parse data sent as java.sql.Types.TIMESTAMP_WITH_TIMEZONE */ public void setTimestampWithTimezoneFormat(String dateTimeFormat) { - loggerExternal.entering(loggerClassName, "setTimestampWithTimezoneFormat", dateTimeFormat); - - this.dateTimeFormatter = DateTimeFormatter.ofPattern(dateTimeFormat); - - loggerExternal.exiting(loggerClassName, "setTimestampWithTimezoneFormat"); } /** @@ -229,11 +152,6 @@ public void setTimestampWithTimezoneFormat(String dateTimeFormat) { * format to parse data sent as java.sql.Types.TIMESTAMP_WITH_TIMEZONE */ public void setTimestampWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { - loggerExternal.entering(loggerClassName, "setTimestampWithTimezoneFormat", new Object[] {dateTimeFormatter}); - - this.dateTimeFormatter = dateTimeFormatter; - - loggerExternal.exiting(loggerClassName, "setTimestampWithTimezoneFormat"); } /** @@ -243,11 +161,6 @@ public void setTimestampWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) * format to parse data sent as java.sql.Types.TIME_WITH_TIMEZONE */ public void setTimeWithTimezoneFormat(String timeFormat) { - loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", timeFormat); - - this.timeFormatter = DateTimeFormatter.ofPattern(timeFormat); - - loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); } /** @@ -257,11 +170,6 @@ public void setTimeWithTimezoneFormat(String timeFormat) { * format to parse data sent as java.sql.Types.TIME_WITH_TIMEZONE */ public void setTimeWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { - loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", new Object[] {dateTimeFormatter}); - - this.timeFormatter = dateTimeFormatter; - - loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); } /* diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 9e02e4d90..859186758 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -125,6 +125,8 @@ public boolean getUseBulkCopyForBatchInsert() throws SQLServerException { /** Sets the prepared statement's useBulkCopyForBatchInsert value. * + * @param useBulkCopyForBatchInsert + * the boolean value * @throws SQLServerException when an error occurs */ public void setUseBulkCopyForBatchInsert(boolean useBulkCopyForBatchInsert) throws SQLServerException { @@ -2558,8 +2560,8 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL // throw a BatchUpdateException with the given error message, and return null for the updateCounts. throw new BatchUpdateException(e.getMessage(), null, 0, null); } - catch (Exception e) { - // If we fail with non-SQLException, fall back to the original batch insert logic. + catch (IllegalArgumentException e) { + // If we fail with IllegalArgumentException, fall back to the original batch insert logic. if (getStatementLogger().isLoggable(java.util.logging.Level.FINE)) { getStatementLogger().fine("Parsing user's Batch Insert SQL Query failed: " + e.toString()); getStatementLogger().fine("Falling back to the original implementation for Batch Insert."); @@ -2622,6 +2624,95 @@ public long[] executeLargeBatch() throws SQLServerException, BatchUpdateExceptio discardLastExecutionResults(); long updateCounts[]; + + localUserSQL = userSQL; + + try { + if (isInsert(localUserSQL) && connection.isAzureDW() && (this.useBulkCopyForBatchInsert)) { + // From the JDBC spec, section 9.1.4 - Making Batch Updates: + // The CallableStatement.executeBatch method (inherited from PreparedStatement) will + // throw a BatchUpdateException if the stored procedure returns anything other than an + // update count or takes OUT or INOUT parameters. + // + // Non-update count results (e.g. ResultSets) are treated as individual batch errors + // when they are encountered in the response. + // + // OUT and INOUT parameter checking is done here, before executing the batch. If any + // OUT or INOUT are present, the entire batch fails. + for (Parameter[] paramValues : batchParamValues) { + for (Parameter paramValue : paramValues) { + if (paramValue.isOutput()) { + throw new BatchUpdateException(SQLServerException.getErrString("R_outParamsNotPermittedinBatch"), null, 0, null); + } + } + } + + if (batchParamValues == null) { + updateCounts = new long[0]; + loggerExternal.exiting(getClassNameLogging(), "executeLargeBatch", updateCounts); + return updateCounts; + } + + String tableName = parseUserSQLForTableNameDW(false, false, false, false); + ArrayList columnList = parseUserSQLForColumnListDW(); + ArrayList valueList = parseUserSQLForValueListDW(false); + + String destinationTableName = tableName; + SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, + connection.getHoldability(), stmtColumnEncriptionSetting); + + // Get destination metadata + try (SQLServerResultSet rs = stmt + .executeQueryInternal("sp_executesql N'SET FMTONLY ON SELECT * FROM " + destinationTableName + " '");) { + + SQLServerBulkBatchInsertRecord batchRecord = new SQLServerBulkBatchInsertRecord(batchParamValues, columnList, valueList, null); + + for (int i = 1; i <= rs.getColumnCount(); i++) { + Column c = rs.getColumn(i); + CryptoMetadata cryptoMetadata = c.getCryptoMetadata(); + int jdbctype; + TypeInfo ti = c.getTypeInfo(); + if (null != cryptoMetadata) { + jdbctype = cryptoMetadata.getBaseTypeInfo().getSSType().getJDBCType().getIntValue(); + } + else { + jdbctype = ti.getSSType().getJDBCType().getIntValue(); + } + batchRecord.addColumnMetadata(i, c.getColumnName(), jdbctype, ti.getPrecision(), ti.getScale()); + } + + SQLServerBulkCopy bcOperation = new SQLServerBulkCopy(connection); + bcOperation.setDestinationTableName(tableName); + bcOperation.setStmtColumnEncriptionSetting(this.getStmtColumnEncriptionSetting()); + bcOperation.setDestinationTableMetadata(rs); + bcOperation.writeToServer((ISQLServerBulkRecord) batchRecord); + bcOperation.close(); + updateCounts = new long[batchParamValues.size()]; + for (int i = 0; i < batchParamValues.size(); ++i) { + updateCounts[i] = 1; + } + + batchParamValues = null; + loggerExternal.exiting(getClassNameLogging(), "executeLargeBatch", updateCounts); + return updateCounts; + } + finally { + if (null != stmt) + stmt.close(); + } + } + } + catch (SQLException e) { + // throw a BatchUpdateException with the given error message, and return null for the updateCounts. + throw new BatchUpdateException(e.getMessage(), null, 0, null); + } + catch (IllegalArgumentException e) { + // If we fail with IllegalArgumentException, fall back to the original batch insert logic. + if (getStatementLogger().isLoggable(java.util.logging.Level.FINE)) { + getStatementLogger().fine("Parsing user's Batch Insert SQL Query failed: " + e.toString()); + getStatementLogger().fine("Falling back to the original implementation for Batch Insert."); + } + } if (batchParamValues == null) updateCounts = new long[0]; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java index 843056d66..21169d188 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java @@ -417,6 +417,65 @@ public void testColumnNameMixAgainstDB() throws Exception { assertEquals(rs.getObject(1), 1); } + @Test + public void testAlColumnsLargeBatch() throws Exception { + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); + f1.setAccessible(true); + f1.set(connection, true); + + String valid = "INSERT INTO " + tableName + " values " + + "(" + + "?, " + + "?, " + + "?, " + + "?, " + + "?, " + + "?, " + + "?, " + + "?, " + + "?, " + + ")"; + + pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); + stmt = (SQLServerStatement) connection.createStatement(); + + Timestamp myTimestamp = new Timestamp(114550L); + + Date d = new Date(114550L); + + pstmt.setInt(1, 1234); + pstmt.setBoolean(2, false); + pstmt.setString(3, "a"); + pstmt.setDate(4, d); + pstmt.setDateTime(5, myTimestamp); + pstmt.setFloat(6, (float) 123.45); + pstmt.setString(7, "b"); + pstmt.setString(8, "varc"); + pstmt.setString(9, "''"); + pstmt.addBatch(); + + pstmt.executeLargeBatch(); + + ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName); + + Object[] expected = new Object[9]; + + expected[0] = 1234; + expected[1] = false; + expected[2] = "a"; + expected[3] = d; + expected[4] = myTimestamp; + expected[5] = 123.45; + expected[6] = "b"; + expected[7] = "varc"; + expected[8] = "''"; + + rs.next(); + for (int i=0; i < expected.length; i++) { + assertEquals(rs.getObject(i + 1).toString(), expected[i].toString()); + } + } + @BeforeEach public void testSetup() throws TestAbortedException, Exception { connection = DriverManager.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); From 66c0b193346a5bd4a911a445c53966fbbd4aa8be Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Fri, 22 Jun 2018 16:22:54 -0700 Subject: [PATCH 71/84] add more tests, make the prepared statement property go away --- .../jdbc/SQLServerPreparedStatement.java | 6 ++- .../BatchExecutionWithBulkCopyTest.java | 50 +++++++++++++++++++ .../preparedStatement/RegressionTest.java | 2 +- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 859186758..ac6289f2f 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -118,7 +118,8 @@ private void setPreparedStatementHandle(int handle) { * Per the description. * @throws SQLServerException when an error occurs */ - public boolean getUseBulkCopyForBatchInsert() throws SQLServerException { + @SuppressWarnings("unused") + private boolean getUseBulkCopyForBatchInsert() throws SQLServerException { checkClosed(); return useBulkCopyForBatchInsert; } @@ -129,7 +130,8 @@ public boolean getUseBulkCopyForBatchInsert() throws SQLServerException { * the boolean value * @throws SQLServerException when an error occurs */ - public void setUseBulkCopyForBatchInsert(boolean useBulkCopyForBatchInsert) throws SQLServerException { + @SuppressWarnings("unused") + private void setUseBulkCopyForBatchInsert(boolean useBulkCopyForBatchInsert) throws SQLServerException { checkClosed(); this.useBulkCopyForBatchInsert = useBulkCopyForBatchInsert; } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java index 21169d188..f18d7df39 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java @@ -319,6 +319,56 @@ public void testNullOrEmptyColumns() throws Exception { } } + @Test + public void testAllFilledColumns() throws Exception { + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); + f1.setAccessible(true); + f1.set(connection, true); + + String valid = "INSERT INTO " + tableName + " values " + + "(" + + "1234, " + + "false, " + + "a, " + + "null, " + + "null, " + + "123.45, " + + "b, " + + "varc, " + + "sadf, " + + ")"; + + pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); + stmt = (SQLServerStatement) connection.createStatement(); + + Timestamp myTimestamp = new Timestamp(114550L); + + Date d = new Date(114550L); + + pstmt.addBatch(); + + pstmt.executeBatch(); + + ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName); + + Object[] expected = new Object[9]; + + expected[0] = 1234; + expected[1] = false; + expected[2] = "a"; + expected[3] = null; + expected[4] = null; + expected[5] = 123.45; + expected[6] = "b"; + expected[7] = "varc"; + expected[8] = "sadf"; + + rs.next(); + for (int i=0; i < expected.length; i++) { + assertEquals(rs.getObject(i + 1), expected[i]); + } + } + @Test public void testSquareBracketAgainstDB() throws Exception { Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java index 7a18ae0fe..ec7342fb9 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java @@ -320,6 +320,7 @@ public void batchWithLargeStringTest() throws SQLException { @Test public void batchWithLargeStringTestUseBulkCopyAPI() throws SQLException { + Connection con = DriverManager.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); Statement stmt = con.createStatement(); SQLServerPreparedStatement pstmt = null; ResultSet rs = null; @@ -359,7 +360,6 @@ public void batchWithLargeStringTestUseBulkCopyAPI() throws SQLException { f1.setAccessible(true); f1.set(con, true); pstmt = (SQLServerPreparedStatement) con.prepareStatement("insert into " + testTable + " values (?,?)"); - pstmt.setUseBulkCopyForBatchInsert(true); // 0,a pstmt.setInt(1, 0); pstmt.setNString(2, values[0]); From 09d79671a2e00f70a75b59a14366a210b2e21796 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Fri, 22 Jun 2018 17:03:02 -0700 Subject: [PATCH 72/84] Feature | Introduce support for "Data Classification Specifications" on fetched resultsets (#709) * Feature | Data Classification Project | Phase 1 (contains temporary skipping 2 bytes) * Feature | Data Classification - Removing extra bytes added before * Feature | Data Classification - Added new test class for testing Data Classification support in the driver * Remove one println * Feature | Repackaged newly added files for Data Classification + improvements in source code * Feature | Changing tokens to bytes instead of int * Feature | Making variables private * Formatted code + dropTable method called from Utils * Feature | Data Classification - Changes as per review comments * Fix | Review comment changes * Change exception codes to follow series * Fix Conflict issue * Added missing Javadocs and headers for all new files --- .../microsoft/sqlserver/jdbc/IOBuffer.java | 38 ++++- .../sqlserver/jdbc/SQLServerConnection.java | 60 +++++++- .../sqlserver/jdbc/SQLServerException.java | 5 + .../sqlserver/jdbc/SQLServerResource.java | 2 + .../sqlserver/jdbc/SQLServerResultSet.java | 14 +- .../sqlserver/jdbc/StreamColumns.java | 99 +++++++++++++ .../dataclassification/ColumnSensitivity.java | 41 ++++++ .../dataclassification/InformationType.java | 52 +++++++ .../jdbc/dataclassification/Label.java | 52 +++++++ .../SensitivityClassification.java | 75 ++++++++++ .../SensitivityProperty.java | 51 +++++++ .../microsoft/sqlserver/jdbc/tdsparser.java | 3 +- .../resultset/DataClassificationTest.java | 139 ++++++++++++++++++ .../sqlserver/testframework/util/Util.java | 19 +++ 14 files changed, 634 insertions(+), 16 deletions(-) create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/dataclassification/ColumnSensitivity.java create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/dataclassification/InformationType.java create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/dataclassification/Label.java create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/dataclassification/SensitivityClassification.java create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/dataclassification/SensitivityProperty.java create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/resultset/DataClassificationTest.java diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index d1aa53d7a..94e62341a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -73,6 +73,9 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; + +import com.microsoft.sqlserver.jdbc.dataclassification.SensitivityClassification; + import java.nio.Buffer; final class TDS { @@ -100,9 +103,11 @@ final class TDS { static final int TDS_DONEPROC = 0xFE; static final int TDS_DONEINPROC = 0xFF; static final int TDS_FEDAUTHINFO = 0xEE; + static final int TDS_SQLRESCOLSRCS = 0xa2; + static final int TDS_SQLDATACLASSIFICATION = 0xa3; // FedAuth - static final int TDS_FEATURE_EXT_FEDAUTH = 0x02; + static final byte TDS_FEATURE_EXT_FEDAUTH = 0x02; static final int TDS_FEDAUTH_LIBRARY_SECURITYTOKEN = 0x01; static final int TDS_FEDAUTH_LIBRARY_ADAL = 0x02; static final int TDS_FEDAUTH_LIBRARY_RESERVED = 0x7F; @@ -112,9 +117,17 @@ final class TDS { static final byte FEDAUTH_INFO_ID_SPN = 0x02; // FedAuthInfoData is the SPN to use for acquiring fed auth token // AE constants - static final int TDS_FEATURE_EXT_AE = 0x04; - static final int MAX_SUPPORTED_TCE_VERSION = 0x01; // max version + // 0x03 is for x_eFeatureExtensionId_Rcs + static final byte TDS_FEATURE_EXT_AE = 0x04; + static final byte MAX_SUPPORTED_TCE_VERSION = 0x01; // max version static final int CUSTOM_CIPHER_ALGORITHM_ID = 0; // max version + // 0x06 is for x_eFeatureExtensionId_LoginToken + // 0x07 is for x_eFeatureExtensionId_ClientSideTelemetry + // Data Classification constants + static final byte TDS_FEATURE_EXT_DATACLASSIFICATION = 0x09; + static final byte DATA_CLASSIFICATION_NOT_ENABLED = 0x00; + static final byte MAX_SUPPORTED_DATA_CLASSIFICATION_VERSION = 0x01; + static final int AES_256_CBC = 1; static final int AEAD_AES_256_CBC_HMAC_SHA256 = 2; static final int AE_METADATA = 0x08; @@ -179,6 +192,8 @@ static final String getTokenName(int tdsTokenType) { return "TDS_DONEINPROC (0xFF)"; case TDS_FEDAUTHINFO: return "TDS_FEDAUTHINFO (0xEE)"; + case TDS_FEATURE_EXT_DATACLASSIFICATION: + return "TDS_FEATURE_EXT_DATACLASSIFICATION (0x09)"; case TDS_FEATURE_EXT_UTF8SUPPORT: return "TDS_FEATURE_EXT_UTF8SUPPORT (0x0A)"; default: @@ -6335,7 +6350,7 @@ final TDSCommand getCommand() { final SQLServerConnection getConnection() { return con; } - + private TDSPacket currentPacket = new TDSPacket(0); private TDSPacket lastPacket = currentPacket; private int payloadOffset = 0; @@ -6344,8 +6359,12 @@ final SQLServerConnection getConnection() { private boolean isStreaming = true; private boolean useColumnEncryption = false; private boolean serverSupportsColumnEncryption = false; + private boolean serverSupportsDataClassification = false; private final byte valueBytes[] = new byte[256]; + + protected SensitivityClassification sensitivityClassification; + private static final AtomicInteger lastReaderID = new AtomicInteger(0); private static int nextReaderID() { @@ -6372,6 +6391,7 @@ private static int nextReaderID() { useColumnEncryption = true; } serverSupportsColumnEncryption = con.getServerSupportsColumnEncryption(); + serverSupportsDataClassification = con.getServerSupportsDataClassification(); } final boolean isColumnEncryptionSettingEnabled() { @@ -6382,6 +6402,10 @@ final boolean getServerSupportsColumnEncryption() { return serverSupportsColumnEncryption; } + final boolean getServerSupportsDataClassification() { + return serverSupportsDataClassification; + } + final void throwInvalidTDS() throws SQLServerException { if (logger.isLoggable(Level.SEVERE)) logger.severe(toString() + " got unexpected value in TDS response at offset:" + payloadOffset); @@ -7073,7 +7097,7 @@ final void skip(int bytesToSkip) throws SQLServerException { } } - final void TryProcessFeatureExtAck(boolean featureExtAckReceived) throws SQLServerException { + final void tryProcessFeatureExtAck(boolean featureExtAckReceived) throws SQLServerException { // in case of redirection, do not check if TDS_FEATURE_EXTENSION_ACK is received or not. if (null != this.con.getRoutingInfo()) { return; @@ -7082,6 +7106,10 @@ final void TryProcessFeatureExtAck(boolean featureExtAckReceived) throws SQLServ if (isColumnEncryptionSettingEnabled() && !featureExtAckReceived) throw new SQLServerException(this, SQLServerException.getErrString("R_AE_NotSupportedByServer"), null, 0, false); } + + final void trySetSensitivityClassification(SensitivityClassification sensitivityClassification) { + this.sensitivityClassification = sensitivityClassification; + } } /** diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 25debf539..be9fa8f50 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -560,6 +560,12 @@ boolean getServerSupportsColumnEncryption() { return serverSupportsColumnEncryption; } + private boolean serverSupportsDataClassification = false; + + boolean getServerSupportsDataClassification() { + return serverSupportsDataClassification; + } + static boolean isWindows; static Map globalSystemColumnEncryptionKeyStoreProviders = new HashMap<>(); static { @@ -3334,9 +3340,9 @@ int writeAEFeatureRequest(boolean write, int len = 6; // (1byte = featureID, 4bytes = featureData length, 1 bytes = Version) if (write) { - tdsWriter.writeByte((byte) TDS.TDS_FEATURE_EXT_AE); // FEATUREEXT_TCE + tdsWriter.writeByte(TDS.TDS_FEATURE_EXT_AE); // FEATUREEXT_TCE tdsWriter.writeInt(1); - tdsWriter.writeByte((byte) TDS.MAX_SUPPORTED_TCE_VERSION); + tdsWriter.writeByte(TDS.MAX_SUPPORTED_TCE_VERSION); } return len; } @@ -3347,7 +3353,6 @@ int writeFedAuthFeatureRequest(boolean write, * if false just calculates the * length */ - assert (fedAuthFeatureExtensionData.libraryType == TDS.TDS_FEDAUTH_LIBRARY_ADAL || fedAuthFeatureExtensionData.libraryType == TDS.TDS_FEDAUTH_LIBRARY_SECURITYTOKEN); @@ -3431,7 +3436,19 @@ int writeFedAuthFeatureRequest(boolean write, } return totalLen; } - + + int writeDataClassificationFeatureRequest(boolean write /* if false just calculates the length */, + TDSWriter tdsWriter) throws SQLServerException { + int len = 6; // 1byte = featureID, 4bytes = featureData length, 1 bytes = Version + if (write) { + // Write Feature ID, length of the version# field and Sensitivity Classification Version# + tdsWriter.writeByte(TDS.TDS_FEATURE_EXT_DATACLASSIFICATION); + tdsWriter.writeInt(1); + tdsWriter.writeByte(TDS.MAX_SUPPORTED_DATA_CLASSIFICATION_VERSION); + } + return len; // size of data written + } + int writeUTF8SupportFeatureRequest(boolean write, TDSWriter tdsWriter /* if false just calculates the length */) throws SQLServerException { int len = 5; // 1byte = featureID, 4bytes = featureData length @@ -4066,7 +4083,7 @@ final void processFeatureExtAck(TDSReader tdsReader) throws SQLServerException { while (featureId != TDS.FEATURE_EXT_TERMINATOR); } - private void onFeatureExtAck(int featureId, + private void onFeatureExtAck(byte featureId, byte[] data) throws SQLServerException { if (null != routingInfo) { return; @@ -4136,10 +4153,33 @@ private void onFeatureExtAck(int featureId, throw new SQLServerException(SQLServerException.getErrString("R_InvalidAEVersionNumber"), null); } - assert supportedTceVersion == TDS.MAX_SUPPORTED_TCE_VERSION; // Client support TCE version 1 serverSupportsColumnEncryption = true; break; } + case TDS.TDS_FEATURE_EXT_DATACLASSIFICATION: { + if (connectionlogger.isLoggable(Level.FINER)) { + connectionlogger.fine(toString() + " Received feature extension acknowledgement for Data Classification."); + } + + if (2 != data.length) { + if (connectionlogger.isLoggable(Level.SEVERE)) { + connectionlogger.severe(toString() + " Unknown token for Data Classification."); + } + throw new SQLServerException(SQLServerException.getErrString("R_UnknownDataClsTokenNumber"), null); + } + + byte supportedDataClassificationVersion = data[0]; + if ((0 == supportedDataClassificationVersion) + || (supportedDataClassificationVersion > TDS.MAX_SUPPORTED_DATA_CLASSIFICATION_VERSION)) { + if (connectionlogger.isLoggable(Level.SEVERE)) { + connectionlogger.severe(toString() + " Invalid version number for Data Classification"); + } + throw new SQLServerException(SQLServerException.getErrString("R_InvalidDataClsVersionNumber"), null); + } + + byte enabled = data[1]; + serverSupportsDataClassification = (enabled == 0) ? false : true; + } case TDS.TDS_FEATURE_EXT_UTF8SUPPORT: { if (connectionlogger.isLoggable(Level.FINER)) { connectionlogger.fine(toString() + " Received feature extension acknowledgement for UTF8 support."); @@ -4439,9 +4479,12 @@ else if (serverMajorVersion >= 9) // Yukon (9.0) --> TDS 7.2 // Prelogin disconn len2 = len2 + writeFedAuthFeatureRequest(false, tdsWriter, fedAuthFeatureExtensionData); } - len2 = len2 + 1; // add 1 to length becaue of FeatureEx terminator - + // Data Classification is always enabled (by default) + len2 += writeDataClassificationFeatureRequest(false, tdsWriter); + len2 = len2 + writeUTF8SupportFeatureRequest(false, tdsWriter); + + len2 = len2 + 1; // add 1 to length because of FeatureEx terminator // Length of entire Login 7 packet tdsWriter.writeInt(len2); @@ -4622,6 +4665,7 @@ else if (serverMajorVersion >= 9) // Yukon (9.0) --> TDS 7.2 // Prelogin disconn writeFedAuthFeatureRequest(true, tdsWriter, fedAuthFeatureExtensionData); } + writeDataClassificationFeatureRequest(true, tdsWriter); writeUTF8SupportFeatureRequest(true, tdsWriter); tdsWriter.writeByte((byte) TDS.FEATURE_EXT_TERMINATOR); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java index 80629cc64..52d80a4a2 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java @@ -72,6 +72,11 @@ public final class SQLServerException extends java.sql.SQLException { static final int DRIVER_ERROR_INTERMITTENT_TLS_FAILED = 7; static final int ERROR_SOCKET_TIMEOUT = 8; static final int ERROR_QUERY_TIMEOUT = 9; + static final int DATA_CLASSIFICATION_INVALID_VERSION = 10; + static final int DATA_CLASSIFICATION_NOT_EXPECTED = 11; + static final int DATA_CLASSIFICATION_INVALID_LABEL_INDEX = 12; + static final int DATA_CLASSIFICATION_INVALID_INFORMATION_TYPE_INDEX = 13; + private int driverErrorCode = DRIVER_ERROR_NONE; final int getDriverErrorCode() { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 483d00c4c..c2ba11f8e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -393,6 +393,8 @@ protected Object[][] getContents() { {"R_invalidSSLProtocol", "SSL Protocol {0} label is not valid. Only TLS, TLSv1, TLSv1.1, and TLSv1.2 are supported."}, {"R_cancelQueryTimeoutPropertyDescription", "The number of seconds to wait to cancel sending a query timeout."}, {"R_invalidCancelQueryTimeout", "The cancel timeout value {0} is not valid."}, + {"R_UnknownDataClsTokenNumber", "Unknown token for Data Classification."}, // From Server + {"R_InvalidDataClsVersionNumber", "Invalid version number {0} for Data Classification."}, // From Server {"R_unknownUTF8SupportValue", "Unknown value for UTF8 support."}, {"R_illegalWKT", "Illegal Well-Known text. Please make sure Well-Known text is valid."}, {"R_illegalTypeForGeometry", "{0} is not supported for Geometry."}, diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java index e3a2296e3..31709297a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java @@ -31,6 +31,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; +import com.microsoft.sqlserver.jdbc.dataclassification.SensitivityClassification; + /** * Indicates the type of the row received from the server */ @@ -212,7 +214,17 @@ protected TDSReader getTDSReader() { } private final FetchBuffer fetchBuffer; - + + /** + * Exposes Data Classification information for the current ResultSet For SQL Servers that do not support Data Classification or results that do + * not fetch any classified columns, this data can be null + * + * @return SensitivityClassification + */ + public SensitivityClassification getSensitivityClassification() { + return tdsReader.sensitivityClassification; + } + /** * Make a new result set * diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/StreamColumns.java b/src/main/java/com/microsoft/sqlserver/jdbc/StreamColumns.java index 3128fb318..e0f5342d9 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/StreamColumns.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/StreamColumns.java @@ -8,6 +8,16 @@ package com.microsoft.sqlserver.jdbc; +import java.util.List; + +import com.microsoft.sqlserver.jdbc.dataclassification.ColumnSensitivity; +import com.microsoft.sqlserver.jdbc.dataclassification.InformationType; +import com.microsoft.sqlserver.jdbc.dataclassification.Label; +import com.microsoft.sqlserver.jdbc.dataclassification.SensitivityClassification; +import com.microsoft.sqlserver.jdbc.dataclassification.SensitivityProperty; + +import java.util.ArrayList; + /** * StreamColumns stores the column meta data for a result set. StreamColumns parses the inbound TDS packet stream to determine column meta data. */ @@ -210,6 +220,95 @@ void setFromTDS(TDSReader tdsReader) throws SQLServerException { this.columns[numColumns] = new Column(typeInfo, columnName, tableName, null); } } + + // Data Classification + if (tdsReader.getServerSupportsDataClassification() && tdsReader.peekTokenType() == TDS.TDS_SQLDATACLASSIFICATION) { + // Read and parse + tdsReader.trySetSensitivityClassification(processDataClassification(tdsReader)); + } + } + + private String readByteString(TDSReader tdsReader) throws SQLServerException { + String value = ""; + int byteLen = (int) tdsReader.readUnsignedByte(); + value = tdsReader.readUnicodeString(byteLen); + return value; + } + + private Label readSensitivityLabel(TDSReader tdsReader) throws SQLServerException { + String name = readByteString(tdsReader); + String id = readByteString(tdsReader); + return new Label(name, id); + } + + private InformationType readSensitivityInformationType(TDSReader tdsReader) throws SQLServerException { + String name = readByteString(tdsReader); + String id = readByteString(tdsReader); + return new InformationType(name, id); + } + + private SensitivityClassification processDataClassification(TDSReader tdsReader) throws SQLServerException { + if (!tdsReader.getServerSupportsDataClassification()) { + tdsReader.throwInvalidTDS(); + } + + int dataClassificationToken = tdsReader.readUnsignedByte(); + assert dataClassificationToken == TDS.TDS_SQLDATACLASSIFICATION; + + SensitivityClassification sensitivityClassification = null; + + // get the label count + int numLabels = tdsReader.readUnsignedShort(); + List

+ * Note: All the data in the returned stream must be read prior to getting the value of any other column. The next call to a getter method + * implicitly closes the stream. Also, a stream may return 0 when the method InputStream.available is called whether + * there is data available or not. + * + * @param parameterIndex + * the first column is 1, the second is 2, ... + * @return a Java input stream that delivers the database column value as a stream of one-byte ASCII characters; if the value is SQL + * NULL, the value returned is null + * @throws SQLServerException + * if the columnIndex is not valid; if a database access error occurs or this method is called on a closed result set + */ + public java.io.InputStream getAsciiStream(int parameterIndex) throws SQLServerException; + + /** + * Retrieves the value of the designated column in the current row of this ResultSet object as a stream of ASCII characters. The + * value can then be read in chunks from the stream. This method is particularly suitable for retrieving large LONGVARCHAR values. + * The JDBC driver will do any necessary conversion from the database format into ASCII. + * + *

+ * Note: All the data in the returned stream must be read prior to getting the value of any other column. The next call to a getter method + * implicitly closes the stream. Also, a stream may return 0 when the method available is called whether there is data + * available or not. + * + * @param parameterName + * the name of the parameter + * @return a Java input stream that delivers the database column value as a stream of one-byte ASCII characters. If the value is SQL + * NULL, the value returned is null. + * @throws SQLServerException + * if the columnLabel is not valid; if a database access error occurs or this method is called on a closed result set + */ + public java.io.InputStream getAsciiStream(String parameterName) throws SQLServerException; + + /** + * Retrieves the value of the column specified as a java.math.BigDecimal object. + * + * @param parameterIndex + * The zero-based ordinal of a column. + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * when an error occurs + */ + public BigDecimal getMoney(int parameterIndex) throws SQLServerException; + + /** + * Retrieves the value of the column specified as a java.math.BigDecimal object. + * + * @param parameterName + * The name of a column. + * @return the column value; if the value is SQL NULL, the value returned is null. + * @throws SQLServerException + * when an error occurs + */ + public BigDecimal getMoney(String parameterName) throws SQLServerException; + + /** + * Retrieves the value of the column specified as a java.math.BigDecimal object. + * + * @param parameterIndex + * The zero-based ordinal of a column. + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * when an error occurs + */ + public BigDecimal getSmallMoney(int parameterIndex) throws SQLServerException; + + /** + * Retrieves the value of the column specified as a java.math.BigDecimal object. + * + * @param parameterName + * The name of a column. + * @return the column value; if the value is SQL NULL, the value returned is null. + * @throws SQLServerException + * when an error occurs + */ + public BigDecimal getSmallMoney(String parameterName) throws SQLServerException; + + /** + * Retrieves the value of the designated column in the current row of this ResultSet object as a stream of uninterpreted bytes. The + * value can then be read in chunks from the stream. This method is particularly suitable for retrieving large LONGVARBINARY values. + * + *

+ * Note: All the data in the returned stream must be read prior to getting the value of any other column. The next call to a getter method + * implicitly closes the stream. Also, a stream may return 0 when the method InputStream.available is called whether + * there is data available or not. + * + * @param parameterIndex + * the first column is 1, the second is 2, ... + * @return a Java input stream that delivers the database column value as a stream of uninterpreted bytes; if the value is SQL NULL, + * the value returned is null + * @throws SQLServerException + * if the columnIndex is not valid; if a database access error occurs or this method is called on a closed result set + */ + public java.io.InputStream getBinaryStream(int parameterIndex) throws SQLServerException; + + /** + * Retrieves the value of the designated column in the current row of this ResultSet object as a stream of uninterpreted + * bytes. The value can then be read in chunks from the stream. This method is particularly suitable for retrieving large + * LONGVARBINARY values. + * + *

+ * Note: All the data in the returned stream must be read prior to getting the value of any other column. The next call to a getter method + * implicitly closes the stream. Also, a stream may return 0 when the method available is called whether there is data + * available or not. + * + * @param parameterName + * the name of the parameter + * @return a Java input stream that delivers the database column value as a stream of uninterpreted bytes; if the value is SQL NULL, + * the result is null + * @throws SQLServerException + * if the columnLabel is not valid; if a database access error occurs or this method is called on a closed result set + */ + public java.io.InputStream getBinaryStream(String parameterName) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Timestamp value. The driver converts this to an SQL TIMESTAMP + * value when it sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param calendar + * a java.util.Calendar + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + * @see #getTimestamp + */ + public void setTimestamp(String parameterName, + java.sql.Timestamp value, + Calendar calendar, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Time value, using the given Calendar object. The driver uses the + * Calendar object to construct an SQL TIME value, which the driver then sends to the database. With a a + * Calendar object, the driver can calculate the time taking into account a custom timezone. If no Calendar object is + * specified, the driver uses the default timezone, which is that of the virtual machine running the application. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param calendar + * the Calendar object the driver will use to construct the time + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + * @see #getTime + */ + public void setTime(String parameterName, + java.sql.Time value, + Calendar calendar, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Date value, using the given Calendar object. The driver uses the + * Calendar object to construct an SQL DATE value, which the driver then sends to the database. With a a + * Calendar object, the driver can calculate the date taking into account a custom timezone. If no Calendar object is + * specified, the driver uses the default timezone, which is that of the virtual machine running the application. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param calendar + * the Calendar object the driver will use to construct the date + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + * @see #getDate + */ + public void setDate(String parameterName, + java.sql.Date value, + Calendar calendar, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given String object. The driver converts this to a SQL NCHAR or + * NVARCHAR or LONGNVARCHAR + * + * @param parameterName + * the name of the parameter to be set + * @param value + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if the driver does not support national character sets; if the driver + * can detect that a data conversion error could occur; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setNString(String parameterName, + String value, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the value of the designated parameter with the given object. + * + *

+ * The given Java object will be converted to the given targetSqlType before being sent to the database. + * + * If the object has a custom mapping (is of a class implementing the interface SQLData), the JDBC driver should call the method + * SQLData.writeSQL to write it to the SQL data stream. If, on the other hand, the object is of a class implementing + * Ref, Blob, Clob, NClob, Struct, java.net.URL, or + * Array, the driver should pass it to the database as a value of the corresponding SQL type. + *

+ * Note that this method may be used to pass database- specific abstract data types. + * + * @param parameterName + * the name of the parameter + * @param value + * the object containing the input parameter value + * @param sqlType + * the SQL type (as defined in java.sql.Types) to be sent to the database. The scale argument may further qualify this type. + * @param decimals + * for java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types, this is the number of digits after the decimal point. For all other + * types, this value will be ignored. + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + * @see java.sql.Types + * @see #getObject + */ + public void setObject(String parameterName, + Object value, + int sqlType, + int decimals, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the value of the designated parameter with the given object. + * + *

+ * The given Java object will be converted to the given targetSqlType before being sent to the database. + * + * If the object has a custom mapping (is of a class implementing the interface SQLData), the JDBC driver should call the method + * SQLData.writeSQL to write it to the SQL data stream. If, on the other hand, the object is of a class implementing + * Ref, Blob, Clob, NClob, Struct, java.net.URL, or + * Array, the driver should pass it to the database as a value of the corresponding SQL type. + *

+ * Note that this method may be used to pass datatabase- specific abstract data types. + * + * @param parameterName + * the name of the parameter + * @param value + * the object containing the input parameter value + * @param targetSqlType + * the SQL type (as defined in java.sql.Types) to be sent to the database. The scale argument may further qualify this type. + * @param precision + * the precision of the column. + * @param scale + * the scale of the column. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + * @see java.sql.Types + * @see #getObject + */ + public void setObject(String parameterName, + Object value, + int targetSqlType, + Integer precision, + int scale) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Timestamp value. The driver converts this to an SQL TIMESTAMP + * value when it sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param scale + * the scale of the parameter + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + * @see #getTimestamp + */ + public void setTimestamp(String parameterName, + java.sql.Timestamp value, + int scale) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Timestamp value. The driver converts this to an SQL TIMESTAMP + * value when it sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param scale + * the scale of the parameter + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + * @see #getTimestamp + */ + public void setTimestamp(String parameterName, + java.sql.Timestamp value, + int scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets parameter parameterName to DateTimeOffset x + * + * @param parameterName + * the name of the parameter + * @param value + * DateTimeOffset value + * @throws SQLServerException + * if an error occurs + */ + public void setDateTimeOffset(String parameterName, + microsoft.sql.DateTimeOffset value) throws SQLServerException; + + /** + * Sets parameter parameterName to DateTimeOffset x + * + * @param parameterName + * the name of the parameter + * @param value + * DateTimeOffset value + * @param scale + * the scale of the parameter + * @throws SQLServerException + * if an error occurs + */ + public void setDateTimeOffset(String parameterName, + microsoft.sql.DateTimeOffset value, + int scale) throws SQLServerException; + + /** + * Sets parameter parameterName to DateTimeOffset x + * + * @param parameterName + * the name of the parameter + * @param value + * DateTimeOffset value + * @param scale + * the scale of the parameter + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if an error occurs + */ + public void setDateTimeOffset(String parameterName, + microsoft.sql.DateTimeOffset value, + int scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Time value. The driver converts this to an SQL TIME value when it + * sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param scale + * the scale of the column + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + * @see #getTime + */ + public void setTime(String parameterName, + java.sql.Time value, + int scale) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Time value. The driver converts this to an SQL TIME value when it + * sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + * @see #getTime + */ + public 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 SQL DATETIME + * value when it sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setDateTime(String parameterName, + java.sql.Timestamp value) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Timestamp value. The driver converts this to an SQL DATETIME + * value when it sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public 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 SQL SMALLDATETIME + * value when it sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setSmallDateTime(String parameterName, + java.sql.Timestamp value) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Timestamp value. The driver converts this to an SQL SMALLDATETIME + * value when it sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setSmallDateTime(String parameterName, + java.sql.Timestamp value, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given String value. The driver converts this to an SQL uniqueIdentifier value + * when it sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param guid + * the parameter value + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setUniqueIdentifier(String parameterName, + String guid) throws SQLServerException; + + /** + * Sets the designated parameter to the given String value. The driver converts this to an SQL uniqueIdentifier value + * when it sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param guid + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setUniqueIdentifier(String parameterName, + String guid, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java array of bytes. The driver converts this to an SQL VARBINARY or + * LONGVARBINARY (depending on the argument's size relative to the driver's limits on VARBINARY values) when it sends it + * to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setBytes(String parameterName, + byte[] value, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java byte value. The driver converts this to an SQL TINYINT value when it + * sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setByte(String parameterName, + byte value, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java String value. The driver converts this to an SQL VARCHAR or + * LONGVARCHAR value (depending on the argument's size relative to the driver's limits on VARCHAR values) when it sends + * it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setString(String parameterName, + String value, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java java.math.BigDecimal value. The driver converts this to an SQL Money + * value. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setMoney(String parameterName, + BigDecimal value) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java java.math.BigDecimal value. The driver converts this to an SQL Money + * value. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setMoney(String parameterName, + BigDecimal value, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java java.math.BigDecimal value. The driver converts this to an SQL + * smallMoney value. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setSmallMoney(String parameterName, + BigDecimal value) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java java.math.BigDecimal value. The driver converts this to an SQL + * smallMoney value. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setSmallMoney(String parameterName, + BigDecimal value, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.math.BigDecimal value. The driver converts this to an SQL NUMERIC + * value when it sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param precision + * the precision of the column + * @param scale + * the scale of the column + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public 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 an SQL NUMERIC + * value when it sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param precision + * the precision of the column + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setBigDecimal(String parameterName, + BigDecimal value, + int precision, + int scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java double value. The driver converts this to an SQL DOUBLE value when it + * sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setDouble(String parameterName, + double value, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java float value. The driver converts this to an SQL FLOAT value when it + * sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setFloat(String parameterName, + float value, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java int value. The driver converts this to an SQL INTEGER value when it + * sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setInt(String parameterName, + int value, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java long value. The driver converts this to an SQL BIGINT value when it + * sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setLong(String parameterName, + long value, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java short value. The driver converts this to an SQL SMALLINT value when + * it sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setShort(String parameterName, + short value, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java boolean value. The driver converts this to an SQL BIT or + * BOOLEAN value when it sends it to the database. + * + * @param parameterName + * the name of the parameter + * @param value + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed + * CallableStatement + */ + public void setBoolean(String parameterName, + boolean value, + boolean forceEncrypt) throws SQLServerException; + + /** + * Populates a table valued parameter passed to a stored procedure with a data table. + * + * @param parameterName + * the name of the parameter + * @param tvpName + * the name of the type TVP + * @param tvpDataTable + * the data table object + * @throws SQLServerException + * when an error occurs + */ + public 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 + * + * @param parameterName + * the name of the parameter + * @param tvpName + * the name of the type TVP + * @param tvpResultSet + * the source result set object + * @throws SQLServerException + * when an error occurs + */ + public 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. + * + * @param parameterName + * the name of the parameter + * @param tvpName + * the name of the type TVP + * @param tvpDataRecord + * ISQLServerDataRecord is used for streaming data and the user decides how to use it. tvpDataRecord is an ISQLServerDataRecord + * object.the source result set object + * @throws SQLServerException + * when an error occurs + */ + public void setStructured(String parameterName, + String tvpName, + ISQLServerDataRecord tvpDataRecord) throws SQLServerException; + + /** + * Registers the parameter in ordinal position index to be of JDBC type sqlType. All OUT parameters must be registered before a stored procedure + * is executed. + *

+ * The JDBC type specified by sqlType for an OUT parameter determines the Java type that must be used in the get method to read the value of that + * parameter. + * + * @param parameterName + * the name of the parameter + * @param sqlType + * the JDBC type code defined by SQLType to use to register the OUT Parameter. + * @param precision + * the sum of the desired number of digits to the left and right of the decimal point. It must be greater than or equal to zero. + * @param scale + * the desired number of digits to the right of the decimal point. It must be greater than or equal to zero. + * @throws SQLServerException + * If any errors occur. + */ + public void registerOutParameter(String parameterName, + 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 registered before a stored procedure + * is executed. + *

+ * The JDBC type specified by sqlType for an OUT parameter determines the Java type that must be used in the get method to read the value of that + * parameter. + * + * @param parameterIndex + * the first column is 1, the second is 2, ... + * @param sqlType + * the JDBC type code defined by SQLType to use to register the OUT Parameter. + * @param precision + * the sum of the desired number of digits to the left and right of the decimal point. It must be greater than or equal to zero. + * @param scale + * the desired number of digits to the right of the decimal point. It must be greater than or equal to zero. + * @throws SQLServerException + * If any errors occur. + */ + public 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 registered before a stored procedure + * is executed. + *

+ * The JDBC type specified by sqlType for an OUT parameter determines the Java type that must be used in the get method to read the value of that + * parameter. + * + * @param parameterIndex + * the first column is 1, the second is 2, ... + * @param sqlType + * the JDBC type code defined by SQLType to use to register the OUT Parameter. + * @param precision + * the sum of the desired number of digits to the left and right of the decimal point. It must be greater than or equal to zero. + * @param scale + * the desired number of digits to the right of the decimal point. It must be greater than or equal to zero. + * @throws SQLServerException + * If any errors occur. + */ + public 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 registered before a stored procedure + * is executed. + *

+ * The JDBC type specified by sqlType for an OUT parameter determines the Java type that must be used in the get method to read the value of that + * parameter. + * + * @param parameterName + * the name of the parameter + * @param sqlType + * the JDBC type code defined by SQLType to use to register the OUT Parameter. + * @param precision + * the sum of the desired number of digits to the left and right of the decimal point. It must be greater than or equal to zero. + * @param scale + * the desired number of digits to the right of the decimal point. It must be greater than or equal to zero. + * @throws SQLServerException + * If any errors occur. + */ + public void registerOutParameter(String parameterName, + int sqlType, + int precision, + int scale) throws SQLServerException; + + /** + * Sets the value of the designated parameter with the given object. + * + *

+ * The given Java object will be converted to the given targetSqlType before being sent to the database. + * + * If the object has a custom mapping (is of a class implementing the interface SQLData), the JDBC driver should call the method + * SQLData.writeSQL to write it to the SQL data stream. If, on the other hand, the object is of a class implementing + * Ref, Blob, Clob, NClob, Struct, java.net.URL, or + * Array, the driver should pass it to the database as a value of the corresponding SQL type. + *

+ * Note that this method may be used to pass datatabase- specific abstract data types. + * + * @param parameterName + * the name of the parameter + * @param value + * the object containing the input parameter value + * @param jdbcType + * the SQL type (as defined in java.sql.Types) to be sent to the database. The scale argument may further qualify this type. + * @param scale + * the scale of the column. + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed * CallableStatement + * @see java.sql.Types + * @see #getObject */ - public microsoft.sql.DateTimeOffset getDateTimeOffset(String parameterName) throws SQLException; + public void setObject(String parameterName, + Object value, + SQLType jdbcType, + int scale, + boolean forceEncrypt) throws SQLServerException; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerCallableStatement42.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerCallableStatement42.java deleted file mode 100644 index 4684ec4ac..000000000 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerCallableStatement42.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.jdbc; - -import java.sql.SQLType; - -/** - * This interface requires all the CallableStatement methods including those are specific to JDBC 4.2 - * - */ -public interface ISQLServerCallableStatement42 extends ISQLServerCallableStatement, ISQLServerPreparedStatement42 { - - public void registerOutParameter(int index, - SQLType sqlType) throws SQLServerException; - - public void registerOutParameter(int index, - SQLType sqlType, - String typeName) throws SQLServerException; - - public void registerOutParameter(int index, - SQLType sqlType, - int scale) throws SQLServerException; - - /** - * Registers the parameter in ordinal position index to be of JDBC type sqlType. All OUT parameters must be registered before a stored procedure - * is executed. - *

- * The JDBC type specified by sqlType for an OUT parameter determines the Java type that must be used in the get method to read the value of that - * parameter. - * - * @param index - * the first parameter is 1, the second is 2,... - * @param sqlType - * the JDBC type code defined by SQLType to use to register the OUT Parameter. - * @param precision - * the sum of the desired number of digits to the left and right of the decimal point. It must be greater than or equal to zero. - * @param scale - * the desired number of digits to the right of the decimal point. It must be greater than or equal to zero. - * @throws SQLServerException - * If any errors occur. - */ - public void registerOutParameter(int index, - SQLType sqlType, - int precision, - int scale) throws SQLServerException; - - public void setObject(String sCol, - Object obj, - SQLType jdbcType) throws SQLServerException; - - public void setObject(String sCol, - Object obj, - SQLType jdbcType, - int scale) throws SQLServerException; - - /** - * Sets the value of the designated parameter with the given object. - * - * @param sCol - * the name of the parameter - * @param obj - * the object containing the input parameter value - * @param jdbcType - * the SQL type to be sent to the database - * @param scale - * scale the desired number of digits to the right of the decimal point. It must be greater than or equal to zero. - * @param forceEncrypt - * true if force encryption is on, false if force encryption is off - * @throws SQLServerException - * If any errors occur. - */ - public void setObject(String sCol, - Object obj, - SQLType jdbcType, - int scale, - boolean forceEncrypt) throws SQLServerException; - - public void registerOutParameter(String parameterName, - SQLType sqlType, - String typeName) throws SQLServerException; - - public void registerOutParameter(String parameterName, - SQLType sqlType, - int scale) throws SQLServerException; - - /** - * Registers the parameter in ordinal position index to be of JDBC type sqlType. All OUT parameters must be registered before a stored procedure - * is executed. - *

- * The JDBC type specified by sqlType for an OUT parameter determines the Java type that must be used in the get method to read the value of that - * parameter. - * - * @param parameterName - * the name of the parameter - * @param sqlType - * the JDBC type code defined by SQLType to use to register the OUT Parameter. - * @param precision - * the sum of the desired number of digits to the left and right of the decimal point. It must be greater than or equal to zero. - * @param scale - * the desired number of digits to the right of the decimal point. It must be greater than or equal to zero. - * @throws SQLServerException - * If any errors occur. - */ - public void registerOutParameter(String parameterName, - SQLType sqlType, - int precision, - int scale) throws SQLServerException; - - public void registerOutParameter(String parameterName, - SQLType sqlType) throws SQLServerException; -} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection.java index 57ea5e94a..7ebf9dcfc 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection.java @@ -1,32 +1,344 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.jdbc; - -import java.sql.SQLException; -import java.util.UUID; - -/** - * - * This interface is implemented by SQLServerConnection Class. - */ -public interface ISQLServerConnection extends java.sql.Connection { - // Transaction types. - // TRANSACTION_SNAPSHOT corresponds to -> SET TRANSACTION ISOLATION LEVEL SNAPSHOT - public final static int TRANSACTION_SNAPSHOT = 0x1000; - - /** - * Gets the connection ID of the most recent connection attempt, regardless of whether the attempt succeeded or failed. - * - * @return 16-byte GUID representing the connection ID of the most recent connection attempt. Or, NULL if there is a failure after the connection - * request is initiated and the pre-login handshake. - * @throws SQLException - * If any errors occur. - */ - public UUID getClientConnectionId() throws SQLException; -} +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + +package com.microsoft.sqlserver.jdbc; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.Statement; +import java.util.UUID; + +/** + * This interface is implemented by {@link SQLServerConnection} and {@link SQLServerConnectionPoolProxy} Classes. + */ +public interface ISQLServerConnection extends java.sql.Connection { + + // Transaction types. + // TRANSACTION_SNAPSHOT corresponds to -> SET TRANSACTION ISOLATION LEVEL SNAPSHOT + public final static int TRANSACTION_SNAPSHOT = 0x1000; + + /** + * Gets the connection ID of the most recent connection attempt, regardless of whether the attempt succeeded or failed. + * + * @return 16-byte GUID representing the connection ID of the most recent connection attempt. Or, NULL if there is a failure after the connection + * request is initiated and the pre-login handshake. + * @throws SQLServerException + * If any errors occur. + */ + public UUID getClientConnectionId() throws SQLServerException; + + /** + * Creates a Statement object that will generate ResultSet objects with the given type, concurrency, and holdability. + * This method is the same as the createStatement method above, but it allows the default result set type, concurrency, and + * holdability to be overridden. + * + * @param nType + * one of the following ResultSet constants: ResultSet.TYPE_FORWARD_ONLY, + * ResultSet.TYPE_SCROLL_INSENSITIVE, or ResultSet.TYPE_SCROLL_SENSITIVE + * @param nConcur + * one of the following ResultSet constants: ResultSet.CONCUR_READ_ONLY or + * ResultSet.CONCUR_UPDATABLE + * @param nHold + * one of the following ResultSet constants: ResultSet.HOLD_CURSORS_OVER_COMMIT or + * ResultSet.CLOSE_CURSORS_AT_COMMIT + * @param stmtColEncSetting + * Specifies how data will be sent and received when reading and writing encrypted columns. + * @return a new Statement object that will generate ResultSet objects with the given type, concurrency, and holdability + * @throws SQLServerException + * if a database access error occurs, this method is called on a closed connection or the given parameters are not + * ResultSet constants indicating type, concurrency, and holdability + */ + public Statement createStatement(int nType, + int nConcur, + int nHold, + SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException; + + /** + * Creates a default PreparedStatement object that has the capability to retrieve auto-generated keys. The given constant tells the + * driver whether it should make auto-generated keys available for retrieval. This parameter is ignored if the SQL statement is not an + * INSERT statement, or an SQL statement able to return auto-generated keys (the list of such statements is vendor-specific). + *

+ * Note: This method is optimized for handling parametric SQL statements that benefit from precompilation. If the driver supports + * precompilation, the method prepareStatement will send the statement to the database for precompilation. Some drivers may not + * support precompilation. In this case, the statement may not be sent to the database until the PreparedStatement object is + * executed. This has no direct effect on users; however, it does affect which methods throw certain SQLExceptions. + *

+ * Result sets created using the returned PreparedStatement object will by default be type TYPE_FORWARD_ONLY and have a + * concurrency level of CONCUR_READ_ONLY. The holdability of the created result sets can be determined by calling + * {@link #getHoldability}. + * + * @param sql + * an SQL statement that may contain one or more '?' IN parameter placeholders + * @param flag + * a flag indicating whether auto-generated keys should be returned; one of Statement.RETURN_GENERATED_KEYS or + * Statement.NO_GENERATED_KEYS + * @param stmtColEncSetting + * Specifies how data will be sent and received when reading and writing encrypted columns. + * @return a new PreparedStatement object, containing the pre-compiled SQL statement, that will have the capability of returning + * auto-generated keys + * @throws SQLServerException + * if a database access error occurs, this method is called on a closed connection or the given parameter is not a + * Statement constant indicating whether auto-generated keys should be returned + */ + public PreparedStatement prepareStatement(String sql, + int flag, + SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException; + + /** + * Creates a default PreparedStatement object capable of returning the auto-generated keys designated by the given array. This array + * contains the indexes of the columns in the target table that contain the auto-generated keys that should be made available. The driver will + * ignore the array if the SQL statement is not an INSERT statement, or an SQL statement able to return auto-generated keys (the list + * of such statements is vendor-specific). + *

+ * An SQL statement with or without IN parameters can be pre-compiled and stored in a PreparedStatement object. This object can then + * be used to efficiently execute this statement multiple times. + *

+ * Note: This method is optimized for handling parametric SQL statements that benefit from precompilation. If the driver supports + * precompilation, the method prepareStatement will send the statement to the database for precompilation. Some drivers may not + * support precompilation. In this case, the statement may not be sent to the database until the PreparedStatement object is + * executed. This has no direct effect on users; however, it does affect which methods throw certain SQLExceptions. + *

+ * Result sets created using the returned PreparedStatement object will by default be type TYPE_FORWARD_ONLY and have a + * concurrency level of CONCUR_READ_ONLY. The holdability of the created result sets can be determined by calling + * {@link #getHoldability}. + * + * @param sql + * an SQL statement that may contain one or more '?' IN parameter placeholders + * @param columnIndexes + * an array of column indexes indicating the columns that should be returned from the inserted row or rows + * @param stmtColEncSetting + * Specifies how data will be sent and received when reading and writing encrypted columns. + * @return a new PreparedStatement object, containing the pre-compiled statement, that is capable of returning the auto-generated + * keys designated by the given array of column indexes + * @throws SQLServerException + * if a database access error occurs or this method is called on a closed connection + */ + public PreparedStatement prepareStatement(String sql, + int[] columnIndexes, + SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException; + + /** + * Creates a default PreparedStatement object capable of returning the auto-generated keys designated by the given array. This array + * contains the names of the columns in the target table that contain the auto-generated keys that should be returned. The driver will ignore the + * array if the SQL statement is not an INSERT statement, or an SQL statement able to return auto-generated keys (the list of such + * statements is vendor-specific). + *

+ * An SQL statement with or without IN parameters can be pre-compiled and stored in a PreparedStatement object. This object can then + * be used to efficiently execute this statement multiple times. + *

+ * Note: This method is optimized for handling parametric SQL statements that benefit from precompilation. If the driver supports + * precompilation, the method prepareStatement will send the statement to the database for precompilation. Some drivers may not + * support precompilation. In this case, the statement may not be sent to the database until the PreparedStatement object is + * executed. This has no direct effect on users; however, it does affect which methods throw certain SQLExceptions. + *

+ * Result sets created using the returned PreparedStatement object will by default be type TYPE_FORWARD_ONLY and have a + * concurrency level of CONCUR_READ_ONLY. The holdability of the created result sets can be determined by calling + * {@link #getHoldability}. + * + * @param sql + * an SQL statement that may contain one or more '?' IN parameter placeholders + * @param columnNames + * an array of column names indicating the columns that should be returned from the inserted row or rows + * @param stmtColEncSetting + * Specifies how data will be sent and received when reading and writing encrypted columns. + * @return a new PreparedStatement object, containing the pre-compiled statement, that is capable of returning the auto-generated + * keys designated by the given array of column names + * @throws SQLServerException + * if a database access error occurs or this method is called on a closed connection + */ + public PreparedStatement prepareStatement(String sql, + String[] columnNames, + SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException; + + /** + * Creates a PreparedStatement object that will generate ResultSet objects with the given type, concurrency, and + * holdability. + *

+ * This method is the same as the prepareStatement method above, but it allows the default result set type, concurrency, and + * holdability to be overridden. + * + * @param sql + * a String object that is the SQL statement to be sent to the database; may contain one or more '?' IN parameters + * @param nType + * one of the following ResultSet constants: ResultSet.TYPE_FORWARD_ONLY, + * ResultSet.TYPE_SCROLL_INSENSITIVE, or ResultSet.TYPE_SCROLL_SENSITIVE + * @param nConcur + * one of the following ResultSet constants: ResultSet.CONCUR_READ_ONLY or + * ResultSet.CONCUR_UPDATABLE + * @param resultSetHoldability + * one of the following ResultSet constants: ResultSet.HOLD_CURSORS_OVER_COMMIT or + * ResultSet.CLOSE_CURSORS_AT_COMMIT + * @param stmtColEncSetting + * Specifies how data will be sent and received when reading and writing encrypted columns. + * @return a new PreparedStatement object, containing the pre-compiled SQL statement, that will generate ResultSet + * objects with the given type, concurrency, and holdability + * @throws SQLServerException + * if a database access error occurs, this method is called on a closed connection or the given parameters are not + * ResultSet constants indicating type, concurrency, and holdability + */ + public PreparedStatement prepareStatement(java.lang.String sql, + int nType, + int nConcur, + int resultSetHoldability, + SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException; + + /** + * Creates a CallableStatement object that will generate ResultSet objects with the given type and concurrency. This + * method is the same as the prepareCall method above, but it allows the default result set type, result set concurrency type and + * holdability to be overridden. + * + * @param sql + * a String object that is the SQL statement to be sent to the database; may contain on or more '?' parameters + * @param nType + * one of the following ResultSet constants: ResultSet.TYPE_FORWARD_ONLY, + * ResultSet.TYPE_SCROLL_INSENSITIVE, or ResultSet.TYPE_SCROLL_SENSITIVE + * @param nConcur + * one of the following ResultSet constants: ResultSet.CONCUR_READ_ONLY or + * ResultSet.CONCUR_UPDATABLE + * @param nHold + * one of the following ResultSet constants: ResultSet.HOLD_CURSORS_OVER_COMMIT or + * ResultSet.CLOSE_CURSORS_AT_COMMIT + * @param stmtColEncSetting + * Specifies how data will be sent and received when reading and writing encrypted columns. + * @return a new CallableStatement object, containing the pre-compiled SQL statement, that will generate ResultSet + * objects with the given type, concurrency, and holdability + * @throws SQLServerException + * if a database access error occurs, this method is called on a closed connection or the given parameters are not + * ResultSet constants indicating type, concurrency, and holdability + */ + public CallableStatement prepareCall(String sql, + int nType, + int nConcur, + int nHold, + SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException; + + /** + * Modifies the setting of the sendTimeAsDatetime connection property. When true, java.sql.Time values will be sent to the server as SQL + * Serverdatetime values. When false, java.sql.Time values will be sent to the server as SQL Servertime values. sendTimeAsDatetime can also be + * modified programmatically with SQLServerDataSource.setSendTimeAsDatetime. The default value for this property may change in a future release. + * + * @param sendTimeAsDateTimeValue + * enables/disables setting the sendTimeAsDatetime connection property. For more information about how the Microsoft JDBC Driver for + * SQL Server configures java.sql.Time values before sending them to the server, see + * Configuring How java.sql.Time Values are Sent to the + * Server. + * + * @throws SQLServerException + * if a database access error occurs + */ + public void setSendTimeAsDatetime(boolean sendTimeAsDateTimeValue) throws SQLServerException; + + /** + * Checks the sendTimeAsDatetime property. + * + * @return boolean value of sendTimeAsDatetime + * + * @throws SQLServerException + * if a database access error occurs + */ + public boolean getSendTimeAsDatetime() throws SQLServerException; + + /** + * Returns the number of currently outstanding prepared statement un-prepare actions. + * + * @return Returns the current value per the description. + */ + public int getDiscardedServerPreparedStatementCount(); + + /** + * Forces the un-prepare requests for any outstanding discarded prepared statements to be executed. + */ + public void closeUnreferencedPreparedStatementHandles(); + + /** + * Returns the behavior for a specific connection instance. If false the first execution will call sp_executesql and not prepare a statement, once + * the second execution happens it will call sp_prepexec and actually setup a prepared statement handle. Following executions will call + * sp_execute. This relieves the need for sp_unprepare on prepared statement close if the statement is only executed once. The default for this + * option can be changed by calling setDefaultEnablePrepareOnFirstPreparedStatementCall(). + * + * @return Returns the current setting per the description. + */ + public boolean getEnablePrepareOnFirstPreparedStatementCall(); + + /** + * Specifies the behavior for a specific connection instance. If value is false the first execution will call sp_executesql and not prepare a + * statement, once the second execution happens it will call sp_prepexec and actually setup a prepared statement handle. Following executions will + * call sp_execute. This relieves the need for sp_unprepare on prepared statement close if the statement is only executed once. + * + * @param value + * Changes the setting per the description. + */ + public void setEnablePrepareOnFirstPreparedStatementCall(boolean value); + + /** + * Returns the behavior for a specific connection instance. This setting controls how many outstanding prepared statement discard actions + * (sp_unprepare) can be outstanding per connection before a call to clean-up the outstanding handles on the server is executed. If the setting is + * {@literal <=} 1, unprepare actions will be executed immedietely on prepared statement close. If it is set to {@literal >} 1, these calls will + * be batched together to avoid overhead of calling sp_unprepare too often. The default for this option can be changed by calling + * getDefaultServerPreparedStatementDiscardThreshold(). + * + * @return Returns the current setting per the description. + */ + public int getServerPreparedStatementDiscardThreshold(); + + /** + * Specifies the behavior for a specific connection instance. This setting controls how many outstanding prepared statement discard actions + * (sp_unprepare) can be outstanding per connection before a call to clean-up the outstanding handles on the server is executed. If the setting is + * {@literal <=} 1 unprepare actions will be executed immedietely on prepared statement close. If it is set to {@literal >} 1 these calls will be + * batched together to avoid overhead of calling sp_unprepare too often. + * + * @param value + * Changes the setting per the description. + */ + public void setServerPreparedStatementDiscardThreshold(int value); + + /** + * Specifies the size of the prepared statement cache for this connection. A value less than 1 means no cache. + * + * @param value + * The new cache size. + * + */ + public void setStatementPoolingCacheSize(int value); + + /** + * Returns the size of the prepared statement cache for this connection. A value less than 1 means no cache. + * + * @return Returns the current setting per the description. + */ + public int getStatementPoolingCacheSize(); + + /** + * Whether statement pooling is enabled or not for this connection. + * + * @return Returns the current setting per the description. + */ + public boolean isStatementPoolingEnabled(); + + /** + * Returns the current number of pooled prepared statement handles. + * + * @return Returns the current setting per the description. + */ + public int getStatementHandleCacheEntryCount(); + + /** + * Disable/enable statement pooling. + * + * @param value + * true to disable statement pooling, false to enable it. + */ + public void setDisableStatementPooling(boolean value); + + /** + * Determine whether statement pooling is disabled. + * + * @return true if statement pooling is disabled, false if it is enabled. + */ + public boolean getDisableStatementPooling(); +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java index d970ad55e..c7dca8ee1 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java @@ -1,32 +1,59 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.jdbc; - -import java.sql.SQLException; -import java.sql.ShardingKey; - -public interface ISQLServerConnection43 extends ISQLServerConnection { - - public void beginRequest() throws SQLException; - - public void endRequest() throws SQLException; - - public void setShardingKey(ShardingKey shardingKey) throws SQLServerException; - - public void setShardingKey(ShardingKey shardingKey, - ShardingKey superShardingKey) throws SQLServerException; - - public boolean setShardingKeyIfValid(ShardingKey shardingKey, - int timeout) throws SQLServerException; - - public boolean setShardingKeyIfValid(ShardingKey shardingKey, - ShardingKey superShardingKey, - int timeout) throws SQLServerException; - -} +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + +package com.microsoft.sqlserver.jdbc; + +import java.sql.SQLException; + +/** + * This interface is implemented by {@link SQLServerConnection43} class. + */ +public interface ISQLServerConnection43 extends ISQLServerConnection { + + /** + * Hints to the driver that a request, an independent unit of work, is beginning on this connection. It backs up the values of the connection + * properties that are modifiable through public methods. Each request is independent of all other requests with regard to state local to the + * connection either on the client or the server. Work done between {@code beginRequest}, {@code endRequest} pairs does not depend on any other + * work done on the connection either as part of another request or outside of any request. A request may include multiple transactions. There may + * be dependencies on committed database state as that is not local to the connection. {@code beginRequest} marks the beginning of the work unit. + *

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

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

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

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

+ * + * @throws SQLException + * if an error occurs + * @see #beginRequest() + */ + @Override + public void endRequest() throws SQLException; +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataRecord.java index 79e109b7c..a4ee9f7cd 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataRecord.java @@ -1,49 +1,51 @@ -/* - * 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; - -/** - * The ISQLServerDataRecord interface can be used to create classes that read in data from any source (such as a file) and allow a structured type to - * be sent to SQL Server tables. - */ - -public interface ISQLServerDataRecord { - /** - * Get the column meta data - * - * @param column - * the first column is 1, the second is 2, and so on - * @return SQLServerMetaData of column - */ - public SQLServerMetaData getColumnMetaData(int column); - - /** - * Get the column count. - * - * @return Set of ordinals for the columns. - */ - public int getColumnCount(); - - /** - * Gets the data for the current row as an array of Objects. - * - * Each Object must match the Java language Type that is used to represent the indicated JDBC data type for the given column. For more - * information, see 'Understanding the JDBC Driver Data Types' for the appropriate mappings. - * - * @return The data for the row. - */ - public Object[] getRowData(); - - /** - * Advances to the next data row. - * - * @return True if rows are available; false if there are no more rows - */ - public boolean next(); -} +/* + * 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; + +/** + * This interface can be used to create classes that read in data from any + * source (such as a file) and allow a structured type to be sent to SQL Server + * tables. + */ +public interface ISQLServerDataRecord { + /** + * Get the column meta data + * + * @param column + * the first column is 1, the second is 2, and so on + * @return SQLServerMetaData of column + */ + public SQLServerMetaData getColumnMetaData(int column); + + /** + * Get the column count. + * + * @return Set of ordinals for the columns. + */ + public int getColumnCount(); + + /** + * Gets the data for the current row as an array of Objects. + * + * Each Object must match the Java language Type that is used to represent + * the indicated JDBC data type for the given column. For more information, + * see 'Understanding the JDBC Driver Data Types' for the appropriate + * mappings. + * + * @return The data for the row. + */ + public Object[] getRowData(); + + /** + * Advances to the next data row. + * + * @return True if rows are available; false if there are no more rows + */ + public boolean next(); +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java index 492559244..08168cac1 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java @@ -8,12 +8,15 @@ package com.microsoft.sqlserver.jdbc; -import javax.sql.CommonDataSource; +import org.ietf.jgss.GSSCredential; /** * A factory to create connections to the data source represented by this object. This interface was added in SQL Server JDBC Driver 3.0. + * + * This interface is implemented by {@link SQLServerDataSource} Class. */ -public interface ISQLServerDataSource extends CommonDataSource { +public interface ISQLServerDataSource extends javax.sql.CommonDataSource { + /** * Sets the application intent. * @@ -112,11 +115,36 @@ public interface ISQLServerDataSource extends CommonDataSource { */ public boolean getEncrypt(); + /** + * Beginning in version 6.0 of the Microsoft JDBC Driver for SQL Server, a new connection property transparentNetworkIPResolution (TNIR) is added + * for transparent connection to Always On availability groups or to a server which has multiple IP addresses associated. When + * transparentNetworkIPResolution is true, the driver attempts to connect to the first IP address available. If the first attempt fails, the + * driver tries to connect to all IP addresses in parallel until the timeout expires, discarding any pending connection attempts when one of them + * succeeds. + *

+ * transparentNetworkIPResolution is ignored if multiSubnetFailover is true + *

+ * transparentNetworkIPResolution is ignored if database mirroring is used + *

+ * transparentNetworkIPResolution is ignored if there are more than 64 IP addresses + * + * @param tnir + * if set to true, the driver attempts to connect to the first IP address available. It is true by default. + */ + public void setTransparentNetworkIPResolution(boolean tnir); + + /** + * Retrieves the TransparentNetworkIPResolution value. + * + * @return if enabled, returns true. Otherwise, false. + */ + public boolean getTransparentNetworkIPResolution(); + /** * Sets a Boolean value that indicates if the trustServerCertificate property is enabled. * * @param e - * true if the server Secure Sockets Layer (SSL) certificate should be automatically trusted when the communication layer is encrypted + * true, if the server Secure Sockets Layer (SSL) certificate should be automatically trusted when the communication layer is encrypted * using SSL. Otherwise, false. */ public void setTrustServerCertificate(boolean e); @@ -128,36 +156,51 @@ public interface ISQLServerDataSource extends CommonDataSource { */ public boolean getTrustServerCertificate(); + /** + * This parameter defines the keystore type for the trustStore. + * + * @param trustStoreType + * A String that contains the trust store type + */ + public void setTrustStoreType(String trustStoreType); + + /** + * Returns the keyStore Type for the trustStore + * + * @return trustStoreType A String that contains the trust store type + */ + public String getTrustStoreType(); + /** * Sets the path (including file name) to the certificate trustStore file. * - * @param st + * @param trustStore * A String that contains the path (including file name) to the certificate trustStore file. */ - public void setTrustStore(String st); + public void setTrustStore(String trustStore); /** * Returns the path (including file name) to the certificate trustStore file. * - * @return A String that contains the path (including file name) to the certificate trustStore file, or null if no value is set. + * @return trustStore A String that contains the path (including file name) to the certificate trustStore file, or null if no value is set. */ public String getTrustStore(); /** * Sets the password that is used to check the integrity of the trustStore data. * - * @param p + * @param trustStorePassword * A String that contains the password that is used to check the integrity of the trustStore data. */ - public void setTrustStorePassword(String p); + public void setTrustStorePassword(String trustStorePassword); /** * Sets the host name to be used in validating the SQL Server Secure Sockets Layer (SSL) certificate. * - * @param host + * @param hostName * A String that contains the host name. */ - public void setHostNameInCertificate(String host); + public void setHostNameInCertificate(String hostName); /** * Returns the host name used in validating the SQL Server Secure Sockets Layer (SSL) certificate. @@ -222,11 +265,11 @@ public interface ISQLServerDataSource extends CommonDataSource { /** * Sets the response buffering mode for connections created by using this SQLServerDataSource object. * - * @param respo + * @param bufferingMode * A String that contains the buffering and streaming mode. The valid mode can be one of the following case-insensitive Strings: full * or adaptive. */ - public void setResponseBuffering(String respo); + public void setResponseBuffering(String bufferingMode); /** * Returns the response buffering mode for this SQLServerDataSource object. @@ -267,6 +310,21 @@ public interface ISQLServerDataSource extends CommonDataSource { */ public boolean getSendStringParametersAsUnicode(); + /** + * Translates the serverName from Unicode to ASCII Compatible Encoding (ACE) + * + * @param serverNameAsACE + * if enabled the servername will be translated to ASCII Compatible Encoding (ACE) + */ + public void setServerNameAsACE(boolean serverNameAsACE); + + /** + * Retrieves if the serverName should be translated from Unicode to ASCII Compatible Encoding (ACE) + * + * @return if enabled, will return true. Otherwise, false. + */ + public boolean getServerNameAsACE(); + /** * Sets the name of the computer that is running SQL Server. * @@ -410,6 +468,21 @@ public interface ISQLServerDataSource extends CommonDataSource { */ public void setAuthenticationScheme(String authenticationScheme); + /** + * sets the authentication mode + * + * @param authentication + * the authentication mode + */ + public void setAuthentication(String authentication); + + /** + * Retrieves the authentication mode + * + * @return the authentication value + */ + public String getAuthentication(); + /** * Sets the server spn * @@ -424,4 +497,297 @@ public interface ISQLServerDataSource extends CommonDataSource { * @return A String that contains the server spn */ public String getServerSpn(); + + /** + * sets GSSCredential + * + * @param userCredential + * the credential + */ + public void setGSSCredentials(GSSCredential userCredential); + + /** + * Retrieves the GSSCredential + * + * @return GSSCredential + */ + public GSSCredential getGSSCredentials(); + + /** + * Sets the access token. + * + * @param accessToken + * to be set in the string property. + */ + public void setAccessToken(String accessToken); + + /** + * Retrieves the access token. + * + * @return the access token. + */ + public String getAccessToken(); + + /** + * Enables/disables Always Encrypted functionality for the data source object. The default is Disabled. + * + * @param columnEncryptionSetting + * Enables/disables Always Encrypted functionality for the data source object. The default is Disabled. + */ + public void setColumnEncryptionSetting(String columnEncryptionSetting); + + /** + * Retrieves the Always Encrypted functionality setting for the data source object. + * + * @return the Always Encrypted functionality setting for the data source object. + */ + public String getColumnEncryptionSetting(); + + /** + * Sets the name that identifies a key store. Only value supported is the "JavaKeyStorePassword" for identifying the Java Key Store. The default + * is null. + * + * @param keyStoreAuthentication + * the name that identifies a key store. + */ + public void setKeyStoreAuthentication(String keyStoreAuthentication); + + /** + * Gets the value of the keyStoreAuthentication setting for the data source object. + * + * @return the value of the keyStoreAuthentication setting for the data source object. + */ + public String getKeyStoreAuthentication(); + + /** + * Sets the password for the Java keystore. Note that, for Java Key Store provider the password for the keystore and the key must be the same. + * Note that, keyStoreAuthentication must be set with "JavaKeyStorePassword". + * + * @param keyStoreSecret + * the password to use for the keystore as well as for the key + */ + public void setKeyStoreSecret(String keyStoreSecret); + + /** + * Sets the location including the file name for the Java keystore. Note that, keyStoreAuthentication must be set with "JavaKeyStorePassword". + * + * @param keyStoreLocation + * the location including the file name for the Java keystore. + */ + public void setKeyStoreLocation(String keyStoreLocation); + + /** + * Retrieves the keyStoreLocation for the Java Key Store. + * + * @return the keyStoreLocation for the Java Key Store. + */ + public String getKeyStoreLocation(); + + /** + * Setting the query timeout + * + * @param queryTimeout + * The number of seconds to wait before a timeout has occurred on a query. The default value is 0, which means infinite timeout. + */ + public void setQueryTimeout(int queryTimeout); + + /** + * Getting the query timeout + * + * @return The number of seconds to wait before a timeout has occurred on a query. + */ + public int getQueryTimeout(); + + /** + * Setting the cancel timeout + * + * @param cancelQueryTimeout + * The number of seconds to wait before we wait for the query timeout to happen. + */ + public void setCancelQueryTimeout(int cancelQueryTimeout); + + /** + * Getting the cancel timeout + * + * @return the number of seconds to wait before we wait for the query timeout to happen. + */ + public int getCancelQueryTimeout(); + + /** + * If this configuration is false the first execution of a prepared statement will call sp_executesql and not prepare a statement, once the second + * execution happens it will call sp_prepexec and actually setup a prepared statement handle. Following executions will call sp_execute. This + * relieves the need for sp_unprepare on prepared statement close if the statement is only executed once. + * + * @param enablePrepareOnFirstPreparedStatementCall + * Changes the setting per the description. + */ + public void setEnablePrepareOnFirstPreparedStatementCall(boolean enablePrepareOnFirstPreparedStatementCall); + + /** + * If this configuration returns false the first execution of a prepared statement will call sp_executesql and not prepare a statement, once the + * second execution happens it will call sp_prepexec and actually setup a prepared statement handle. Following executions will call sp_execute. + * This relieves the need for sp_unprepare on prepared statement close if the statement is only executed once. + * + * @return Returns the current setting per the description. + */ + public boolean getEnablePrepareOnFirstPreparedStatementCall(); + + /** + * This setting controls how many outstanding prepared statement discard actions (sp_unprepare) can be outstanding per connection before a call to + * clean-up the outstanding handles on the server is executed. If the setting is {@literal <=} 1 unprepare actions will be executed immedietely on + * prepared statement close. If it is set to {@literal >} 1 these calls will be batched together to avoid overhead of calling sp_unprepare too + * often. + * + * @param serverPreparedStatementDiscardThreshold + * Changes the setting per the description. + */ + public void setServerPreparedStatementDiscardThreshold(int serverPreparedStatementDiscardThreshold); + + /** + * This setting controls how many outstanding prepared statement discard actions (sp_unprepare) can be outstanding per connection before a call to + * clean-up the outstanding handles on the server is executed. If the setting is {@literal <=} 1 unprepare actions will be executed immedietely on + * prepared statement close. If it is set to {@literal >} 1 these calls will be batched together to avoid overhead of calling sp_unprepare too + * often. + * + * @return Returns the current setting per the description. + */ + public int getServerPreparedStatementDiscardThreshold(); + + /** + * Specifies the size of the prepared statement cache for this connection. A value less than 1 means no cache. + * + * @param statementPoolingCacheSize + * Changes the setting per the description. + */ + public void setStatementPoolingCacheSize(int statementPoolingCacheSize); + + /** + * Returns the size of the prepared statement cache for this connection. A value less than 1 means no cache. + * + * @return Returns the current setting per the description. + */ + public int getStatementPoolingCacheSize(); + + /** + * Disable/enable statement pooling. + * + * @param disableStatementPooling + * true to disable statement pooling, false to enable it. + */ + public void setDisableStatementPooling(boolean disableStatementPooling); + + /** + * Determine whether statement pooling is disabled. + * + * @return true if statement pooling is disabled, false if it is enabled. + */ + public boolean getDisableStatementPooling(); + + /** + * Setting the socket timeout + * + * @param socketTimeout + * The number of milliseconds to wait before a timeout is occurred on a socket read or accept. The default value is 0, which means + * infinite timeout. + */ + public void setSocketTimeout(int socketTimeout); + + /** + * Getting the socket timeout + * + * @return The number of milliseconds to wait before a timeout is occurred on a socket read or accept. + */ + public int getSocketTimeout(); + + /** + * Sets the login configuration file for Kerberos authentication. This overrides the default configuration SQLJDBCDriver + * + * @param configurationName + * the configuration name + */ + public void setJASSConfigurationName(String configurationName); + + /** + * Retrieves the login configuration file for Kerberos authentication. + * + * @return login configuration file name + */ + public String getJASSConfigurationName(); + + /** + * Enables Fips Mode on the connection For FIPS enabled JVM this property should be true. + * + * @param fips + * Boolean property to enable/disable fips + */ + public void setFIPS(boolean fips); + + /** + * Retrieves value of connection property "fips" For FIPS enabled JVM this property should be true. + * + * @return fips boolean value + */ + public boolean getFIPS(); + + /** + * Sets the sslProtocol property for connection Set this value to specify TLS protocol keyword. + * + * Acceptable values are: TLS, TLSv1, TLSv1.1, and TLSv1.2. + * + * @param sslProtocol + * Value for SSL Protocol to be set. + */ + public void setSSLProtocol(String sslProtocol); + + /** + * Retrieves value of connection property 'sslProtocol' + * + * @return sslProtocol property value + */ + public String getSSLProtocol(); + + /** + * Sets the connection property 'trustManagerClass' on the connection + * + * @param trustManagerClass + * The fully qualified class name of a custom javax.net.ssl.TrustManager. + */ + public void setTrustManagerClass(String trustManagerClass); + + /** + * Retrieves value for the connection property 'trustManagerClass' + * + * @return trustManagerClass property value + */ + public String getTrustManagerClass(); + + /** + * Sets Constructor Arguments to be provided on constructor of 'trustManagerClass' + * + * @param trustManagerConstructorArg + * 'trustManagerClass' constructor arguments + */ + public void setTrustManagerConstructorArg(String trustManagerConstructorArg); + + /** + * Retrieves value for the connection property 'trustManagerConstructorArg' + * + * @return trustManagerConstructorArg property value + */ + public String getTrustManagerConstructorArg(); + + /** + * Getting the use Bulk Copy API for Batch Insert + * + * @return whether the driver should use Bulk Copy API for Batch Insert operations. + */ + public boolean getUseBulkCopyForBatchInsert(); + + /** + * Setting the use Bulk Copy API for Batch Insert + * + * @param useBulkCopyForBatchInsert + * indicates whether Bulk Copy API should be used for Batch Insert operations. + */ + public void setUseBulkCopyForBatchInsert(boolean useBulkCopyForBatchInsert); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerPreparedStatement.java index ee954f151..17fc6b558 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerPreparedStatement.java @@ -8,8 +8,14 @@ package com.microsoft.sqlserver.jdbc; -import java.sql.SQLException; +import java.math.BigDecimal; +import java.sql.ParameterMetaData; +import java.sql.ResultSet; +import java.sql.SQLType; +/** + * This interface is implemented by {@link SQLServerPreparedStatement} class. + */ public interface ISQLServerPreparedStatement extends java.sql.PreparedStatement, ISQLServerStatement { /** * Sets the designated parameter to the given microsoft.sql.DateTimeOffset value. @@ -18,10 +24,819 @@ public interface ISQLServerPreparedStatement extends java.sql.PreparedStatement, * the first parameter is 1, the second is 2, ... * @param x * the parameter value - * @throws SQLException + * @throws SQLServerException * if parameterIndex does not correspond to a parameter marker in the SQL statement; if a database access error occurs or this method * is called on a closed PreparedStatement */ public void setDateTimeOffset(int parameterIndex, - microsoft.sql.DateTimeOffset x) throws SQLException; + microsoft.sql.DateTimeOffset x) throws SQLServerException; + + /** + * Sets the value of the designated parameter with the given object. + * + * This method is similar to {@link #setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength)}, except that it assumes a + * scale of zero. + *

+ * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the object containing the input parameter value + * @param targetSqlType + * the SQL type to be sent to the database + * @param precision + * the precision of the column + * @param scale + * the scale of the column + * @throws SQLServerException + * if parameterIndex does not correspond to a parameter marker in the SQL statement; if a database access error occurs or this method + * is called on a closed {@code PreparedStatement} + */ + public void setObject(int parameterIndex, + Object x, + SQLType targetSqlType, + Integer precision, + Integer scale) throws SQLServerException; + + /** + * Sets the value of the designated parameter with the given object. + * + * This method is similar to {@link #setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength)}, except that it assumes a + * scale of zero. + *

+ * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the object containing the input parameter value + * @param targetSqlType + * the SQL type to be sent to the database + * @param precision + * the precision of the column + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * if parameterIndex does not correspond to a parameter marker in the SQL statement; if a database access error occurs or this method + * is called on a closed {@code PreparedStatement} + */ + public void setObject(int parameterIndex, + Object x, + SQLType targetSqlType, + Integer precision, + Integer scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * The server handle for this prepared statement. If a value {@literal <} 1 is returned no handle has been created. + * + * @return Per the description. + * @throws SQLServerException + * when an error occurs + */ + public int getPreparedStatementHandle() throws SQLServerException; + + /** + * Sets the designated parameter to the given java.math.BigDecimal value. The driver converts this to an SQL NUMERIC + * value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param precision + * the precision of the column + * @param scale + * the scale of the column + * @throws SQLServerException + * when an error occurs + */ + public void setBigDecimal(int parameterIndex, + BigDecimal x, + int precision, + int scale) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.math.BigDecimal value. The driver converts this to an SQL NUMERIC + * value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param precision + * the precision of the column + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setBigDecimal(int parameterIndex, + BigDecimal x, + int precision, + int scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.math.BigDecimal value. The driver converts this to an SQL NUMERIC + * value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @throws SQLServerException + * when an error occurs + */ + public void setMoney(int parameterIndex, + BigDecimal x) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.math.BigDecimal value. The driver converts this to an SQL NUMERIC + * value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setMoney(int parameterIndex, + BigDecimal x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.math.BigDecimal value. The driver converts this to an SQL NUMERIC + * value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @throws SQLServerException + * when an error occurs + */ + public void setSmallMoney(int parameterIndex, + BigDecimal x) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.math.BigDecimal value. The driver converts this to an SQL NUMERIC + * value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setSmallMoney(int parameterIndex, + BigDecimal x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java boolean value. The driver converts this to an SQL BIT or + * BOOLEAN value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setBoolean(int parameterIndex, + boolean x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java byte value. The driver converts this to an SQL TINYINT value when it + * sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setByte(int parameterIndex, + byte x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java array of bytes. The driver converts this to an SQL VARBINARY or + * LONGVARBINARY (depending on the argument's size relative to the driver's limits on VARBINARY values) when it sends it + * to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setBytes(int parameterIndex, + byte x[], + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given String. The driver converts this to an SQL GUID + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param guid + * string representation of the uniqueIdentifier value + * @throws SQLServerException + * when an error occurs + */ + public void setUniqueIdentifier(int parameterIndex, + String guid) throws SQLServerException; + + /** + * Sets the designated parameter to the given String. The driver converts this to an SQL GUID + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param guid + * string representation of the uniqueIdentifier value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setUniqueIdentifier(int parameterIndex, + String guid, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java double value. The driver converts this to an SQL DOUBLE value when it + * sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setDouble(int parameterIndex, + double x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java float value. The driver converts this to an SQL REAL value when it + * sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setFloat(int parameterIndex, + float x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given microsoft.sql.Geometry Class object. The driver converts this to an SQL + * REAL value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @throws SQLServerException + * when an error occurs + */ + public void setGeometry(int parameterIndex, + Geometry x) throws SQLServerException; + + /** + * Sets the designated parameter to the given microsoft.sql.Geography Class object. The driver converts this to an SQL + * REAL value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @throws SQLServerException + * when an error occurs + */ + public void setGeography(int parameterIndex, + Geography x) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java int value. The driver converts this to an SQL INTEGER value when it + * sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param value + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setInt(int parameterIndex, + int value, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java long value. The driver converts this to an SQL BIGINT value when it + * sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setLong(int parameterIndex, + long x, + boolean forceEncrypt) throws SQLServerException; + + /** + *

+ * Sets the value of the designated parameter with the given object. + * + *

+ * The given Java object will be converted to the given targetSqlType before being sent to the database. + * + * If the object has a custom mapping (is of a class implementing the interface SQLData), the JDBC driver should call the method + * SQLData.writeSQL to write it to the SQL data stream. If, on the other hand, the object is of a class implementing + * Ref, Blob, Clob, NClob, Struct, java.net.URL, or + * Array, the driver should pass it to the database as a value of the corresponding SQL type. + * + *

+ * Note that this method may be used to pass database-specific abstract data types. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the object containing the input parameter value + * @param targetSqlType + * the SQL type (as defined in java.sql.Types) to be sent to the database. The scale argument may further qualify this type. + * @param precision + * the precision of the column + * @param scale + * scale of the column + * @throws SQLServerException + * when an error occurs + */ + public void setObject(int parameterIndex, + Object x, + int targetSqlType, + Integer precision, + int scale) throws SQLServerException; + + /** + *

+ * Sets the value of the designated parameter with the given object. + * + *

+ * The given Java object will be converted to the given targetSqlType before being sent to the database. + * + * If the object has a custom mapping (is of a class implementing the interface SQLData), the JDBC driver should call the method + * SQLData.writeSQL to write it to the SQL data stream. If, on the other hand, the object is of a class implementing + * Ref, Blob, Clob, NClob, Struct, java.net.URL, or + * Array, the driver should pass it to the database as a value of the corresponding SQL type. + * + *

+ * Note that this method may be used to pass database-specific abstract data types. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the object containing the input parameter value + * @param targetSqlType + * the SQL type (as defined in java.sql.Types) to be sent to the database. The scale argument may further qualify this type. + * @param precision + * the precision of the column + * @param scale + * scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setObject(int parameterIndex, + Object x, + int targetSqlType, + Integer precision, + int scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java short value. The driver converts this to an SQL SMALLINT value when + * it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setShort(int parameterIndex, + short x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given Java String value. The driver converts this to an SQL VARCHAR or + * LONGVARCHAR value (depending on the argument's size relative to the driver's limits on VARCHAR values) when it sends + * it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param str + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setString(int parameterIndex, + String str, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given String object. The driver converts this to a SQL NCHAR or + * NVARCHAR or LONGNVARCHAR value (depending on the argument's size relative to the driver's limits on + * NVARCHAR values) when it sends it to the database. + * + * @param parameterIndex + * of the first parameter is 1, the second is 2, ... + * @param value + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setNString(int parameterIndex, + String value, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Time value + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param scale + * the scale of the column + * @throws SQLServerException + * when an error occurs + */ + public void setTime(int parameterIndex, + java.sql.Time x, + int scale) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Time value + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setTime(int parameterIndex, + java.sql.Time x, + int scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Timestamp value + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param scale + * the scale of the column + * @throws SQLServerException + * when an error occurs + */ + public void setTimestamp(int parameterIndex, + java.sql.Timestamp x, + int scale) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Timestamp value + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setTimestamp(int parameterIndex, + java.sql.Timestamp x, + int scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given microsoft.sql.DatetimeOffset value + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param scale + * the scale of the column + * @throws SQLServerException + * when an error occurs + */ + public void setDateTimeOffset(int parameterIndex, + microsoft.sql.DateTimeOffset x, + int scale) throws SQLServerException; + + /** + * Sets the designated parameter to the given microsoft.sql.DatetimeOffset value + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setDateTimeOffset(int parameterIndex, + microsoft.sql.DateTimeOffset x, + int scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Timestamp value + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @throws SQLServerException + * when an error occurs + */ + public void setDateTime(int parameterIndex, + java.sql.Timestamp x) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Timestamp value + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setDateTime(int parameterIndex, + java.sql.Timestamp x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Timestamp value + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @throws SQLServerException + * when an error occurs + */ + public void setSmallDateTime(int parameterIndex, + java.sql.Timestamp x) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Timestamp value + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setSmallDateTime(int parameterIndex, + java.sql.Timestamp x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Populates a table valued parameter with a data table + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param tvpName + * the name of the table valued parameter + * @param tvpDataTable + * the source datatable object + * @throws SQLServerException + * when an error occurs + */ + public void setStructured(int parameterIndex, + String tvpName, + SQLServerDataTable tvpDataTable) throws SQLServerException; + + /** + * Populates a table valued parameter with a data table + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param tvpName + * the name of the table valued parameter + * @param tvpResultSet + * the source resultset object + * @throws SQLServerException + * when an error occurs + */ + public void setStructured(int parameterIndex, + String tvpName, + ResultSet tvpResultSet) throws SQLServerException; + + /** + * Populates a table valued parameter with a data table + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param tvpName + * the name of the table valued parameter + * @param tvpBulkRecord + * an ISQLServerDataRecord object + * @throws SQLServerException + * when an error occurs + */ + public void setStructured(int parameterIndex, + String tvpName, + ISQLServerDataRecord tvpBulkRecord) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Date value, using the given Calendar object. The driver uses the + * Calendar object to construct an SQL DATE value, which the driver then sends to the database. With a + * Calendar object, the driver can calculate the date taking into account a custom timezone. If no Calendar object is + * specified, the driver uses the default timezone, which is that of the virtual machine running the application. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param cal + * the Calendar object the driver will use to construct the date + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setDate(int parameterIndex, + java.sql.Date x, + java.util.Calendar cal, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Time value. The driver converts this to an SQL TIME value when it + * sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param cal + * the Calendar object the driver will use to construct the date + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setTime(int parameterIndex, + java.sql.Time x, + java.util.Calendar cal, + boolean forceEncrypt) throws SQLServerException; + + /** + * Sets the designated parameter to the given java.sql.Timestamp value. The driver converts this to an SQL TIMESTAMP + * value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param cal + * the Calendar object the driver will use to construct the date + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void setTimestamp(int parameterIndex, + java.sql.Timestamp x, + java.util.Calendar cal, + boolean forceEncrypt) throws SQLServerException; + + /** + * Returns parameter metadata for the prepared statement. + * + * @param forceRefresh: + * If true the cache will not be used to retrieve the metadata. + * + * @return Per the description. + * + * @throws SQLServerException + * when an error occurs + */ + public ParameterMetaData getParameterMetaData(boolean forceRefresh) throws SQLServerException; + } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerPreparedStatement42.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerPreparedStatement42.java deleted file mode 100644 index 229af83ee..000000000 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerPreparedStatement42.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.jdbc; - -import java.sql.SQLType; - -/** - * This interface requires all the PreparedStatement methods including those are specific to JDBC 4.2 - * - */ -public interface ISQLServerPreparedStatement42 extends ISQLServerPreparedStatement { - - public void setObject(int index, - Object obj, - SQLType jdbcType) throws SQLServerException; - - public void setObject(int parameterIndex, - Object x, - SQLType targetSqlType, - int scaleOrLength) throws SQLServerException; - - /** - * Sets the value of the designated parameter with the given object. - * - * This method is similar to {@link #setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength)}, except that it assumes a - * scale of zero. - *

- * The default implementation will throw {@code SQLFeatureNotSupportedException} - * - * @param parameterIndex - * the first parameter is 1, the second is 2, ... - * @param x - * the object containing the input parameter value - * @param targetSqlType - * the SQL type to be sent to the database - * @param precision - * the precision of the column - * @param scale - * the scale of the column - * @throws SQLServerException - * if parameterIndex does not correspond to a parameter marker in the SQL statement; if a database access error occurs or this method - * is called on a closed {@code PreparedStatement} - */ - public void setObject(int parameterIndex, - Object x, - SQLType targetSqlType, - Integer precision, - Integer scale) throws SQLServerException; - - /** - * Sets the value of the designated parameter with the given object. - * - * This method is similar to {@link #setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength)}, except that it assumes a - * scale of zero. - *

- * The default implementation will throw {@code SQLFeatureNotSupportedException} - * - * @param parameterIndex - * the first parameter is 1, the second is 2, ... - * @param x - * the object containing the input parameter value - * @param targetSqlType - * the SQL type to be sent to the database - * @param precision - * the precision of the column - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterIndex does not correspond to a parameter marker in the SQL statement; if a database access error occurs or this method - * is called on a closed {@code PreparedStatement} - */ - public void setObject(int parameterIndex, - Object x, - SQLType targetSqlType, - Integer precision, - Integer scale, - boolean forceEncrypt) throws SQLServerException; -} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSet.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSet.java index d0bab83cf..e9fa6ccb6 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSet.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSet.java @@ -8,8 +8,15 @@ package com.microsoft.sqlserver.jdbc; -import java.sql.SQLException; +import java.math.BigDecimal; +import java.sql.SQLType; +import java.util.Calendar; +import com.microsoft.sqlserver.jdbc.dataclassification.SensitivityClassification; + +/** + * This interface is implemented by {@link SQLServerResultSet} class. + */ public interface ISQLServerResultSet extends java.sql.ResultSet { public static final int TYPE_SS_DIRECT_FORWARD_ONLY = 2003; // TYPE_FORWARD_ONLY + 1000 @@ -23,16 +30,195 @@ public interface ISQLServerResultSet extends java.sql.ResultSet { public static final int CONCUR_SS_SCROLL_LOCKS = 1009; // CONCUR_UPDATABLE + 1 public static final int CONCUR_SS_OPTIMISTIC_CCVAL = 1010; // CONCUR_UPDATABLE + 2 + /** + * Retrieves the value of the designated column in the current row of this ResultSet object as a com.microsoft.sqlserver.jdbc.Geometry object in + * the Java programming language. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * when an error occurs + */ + public Geometry getGeometry(int columnIndex) throws SQLServerException; + + /** + * Retrieves the value of the designated column in the current row of this ResultSet object as a com.microsoft.sqlserver.jdbc.Geometry object in + * the Java programming language. + * + * @param columnName + * the name of the column + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * when an error occurs + */ + public Geometry getGeometry(String columnName) throws SQLServerException; + + /** + * Retrieves the value of the designated column in the current row of this ResultSet object as a com.microsoft.sqlserver.jdbc.Geography object in + * the Java programming language. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * when an error occurs + */ + public Geography getGeography(int columnIndex) throws SQLServerException; + + /** + * Retrieves the value of the designated column in the current row of this ResultSet object as a com.microsoft.sqlserver.jdbc.Geography object in + * the Java programming language. + * + * @param columnName + * the name of the column + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * when an error occurs + */ + public Geography getGeography(String columnName) throws SQLServerException; + + /** + * Retrieves the value of the designated column in the current row of this ResultSet object as a String object in the Java programming language. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * when an error occurs + */ + public String getUniqueIdentifier(int columnIndex) throws SQLServerException; + + /** + * Retrieves the value of the designated column in the current row of this ResultSet object as a String object in the Java programming language. + * + * @param columnLabel + * the name of the column + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * when an error occurs + */ + public String getUniqueIdentifier(String columnLabel) throws SQLServerException; + + /** + * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming + * language. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * when an error occurs + */ + public java.sql.Timestamp getDateTime(int columnIndex) throws SQLServerException; + + /** + * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming + * language. + * + * @param columnName + * is the name of the column + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * If any errors occur. + */ + public java.sql.Timestamp getDateTime(String columnName) throws SQLServerException; + + /** + * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming + * language. This method uses the given calendar to construct an appropriate millisecond value for the timestamp if the underlying database does + * not store timezone information. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param cal + * the java.util.Calendar object to use in constructing the dateTime + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * If any errors occur. + */ + public java.sql.Timestamp getDateTime(int columnIndex, + Calendar cal) throws SQLServerException; + + /** + * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming + * language. This method uses the given calendar to construct an appropriate millisecond value for the timestamp if the underlying database does + * not store timezone information. + * + * @param colName + * the label for the column specified with the SQL AS clause. If the SQL AS clause was not specified, then the label is the name of the + * column + * @param cal + * the java.util.Calendar object to use in constructing the dateTime + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * If any errors occur. + */ + public java.sql.Timestamp getDateTime(String colName, + Calendar cal) throws SQLServerException; + + /** + * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming + * language. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * when an error occurs + */ + public java.sql.Timestamp getSmallDateTime(int columnIndex) throws SQLServerException; + + /** + * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming + * language. + * + * @param columnName + * is the name of a column. + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * If any errors occur. + */ + public java.sql.Timestamp getSmallDateTime(String columnName) throws SQLServerException; + + /** + * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming + * language. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param cal + * the java.util.Calendar object to use in constructing the smalldateTime + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * If any errors occur. + */ + public java.sql.Timestamp getSmallDateTime(int columnIndex, + Calendar cal) throws SQLServerException; + + /** + * + * @param colName + * The name of a column + * @param cal + * the java.util.Calendar object to use in constructing the smalldateTime + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * If any errors occur. + */ + public java.sql.Timestamp getSmallDateTime(String colName, + Calendar cal) throws SQLServerException; + /** * Retrieves the value of the designated column as a microsoft.sql.DateTimeOffset object, given a zero-based column ordinal. * * @param columnIndex * The zero-based ordinal of a column. * @return A DateTimeOffset Class object. - * @throws SQLException + * @throws SQLServerException * when an error occurs */ - public microsoft.sql.DateTimeOffset getDateTimeOffset(int columnIndex) throws SQLException; + public microsoft.sql.DateTimeOffset getDateTimeOffset(int columnIndex) throws SQLServerException; /** * Retrieves the value of the column specified as a microsoft.sql.DateTimeOffset object, given a column name. @@ -40,10 +226,54 @@ public interface ISQLServerResultSet extends java.sql.ResultSet { * @param columnName * The name of a column. * @return A DateTimeOffset Class object. - * @throws SQLException + * @throws SQLServerException + * when an error occurs + */ + public microsoft.sql.DateTimeOffset getDateTimeOffset(String columnName) throws SQLServerException; + + /** + * Retrieves the value of the column specified as a java.math.BigDecimal object. + * + * @param columnIndex + * The zero-based ordinal of a column. + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException * when an error occurs */ - public microsoft.sql.DateTimeOffset getDateTimeOffset(String columnName) throws SQLException; + public BigDecimal getMoney(int columnIndex) throws SQLServerException; + + /** + * Retrieves the value of the column specified as a java.math.BigDecimal object. + * + * @param columnName + * is the name of a column. + * @return the column value; if the value is SQL NULL, the value returned is null. + * @throws SQLServerException + * If any errors occur. + */ + public BigDecimal getMoney(String columnName) throws SQLServerException; + + /** + * Retrieves the value of the column specified as a java.math.BigDecimal object. + * + * @param columnIndex + * The zero-based ordinal of a column. + * @return the column value; if the value is SQL NULL, the value returned is null + * @throws SQLServerException + * If any errors occur. + */ + public BigDecimal getSmallMoney(int columnIndex) throws SQLServerException; + + /** + * Retrieves the value of the column specified as a java.math.BigDecimal object. + * + * @param columnName + * is the name of a column. + * @return the column value; if the value is SQL NULL, the value returned is null. + * @throws SQLServerException + * If any errors occur. + */ + public BigDecimal getSmallMoney(String columnName) throws SQLServerException; /** * Updates the value of the column specified to the DateTimeOffset Class value, given a zero-based column ordinal. @@ -52,11 +282,11 @@ public interface ISQLServerResultSet extends java.sql.ResultSet { * The zero-based ordinal of a column. * @param x * A DateTimeOffset Class object. - * @throws SQLException + * @throws SQLServerException * when an error occurs */ public void updateDateTimeOffset(int index, - microsoft.sql.DateTimeOffset x) throws SQLException; + microsoft.sql.DateTimeOffset x) throws SQLServerException; /** * Updates the value of the column specified to the DateTimeOffset Class value, given a column name. @@ -65,10 +295,1408 @@ public void updateDateTimeOffset(int index, * The name of a column. * @param x * A DateTimeOffset Class object. - * @throws SQLException + * @throws SQLServerException * when an error occurs */ public void updateDateTimeOffset(String columnName, - microsoft.sql.DateTimeOffset x) throws SQLException; + microsoft.sql.DateTimeOffset x) throws SQLServerException; + + /** + * Updates the designated column with an {@code Object} value. + * + * The updater methods are used to update column values in the current row or the insert row. The updater methods do not update the underlying + * database; instead the {@code updateRow} or {@code insertRow} methods are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param precision + * the precision of the column + * @param scale + * the scale of the column + * @throws SQLServerException + * when an error occurs + */ + public void updateObject(int index, + Object x, + int precision, + int scale) throws SQLServerException; + + /** + * Updates the designated column with an Object value. The updater methods are used to update column values in the current row or the insert row. + * The updater methods do not update the underlying database; instead the updateRow or insertRow methods are called to update the database. If the + * second argument is an InputStream then the stream must contain the number of bytes specified by scaleOrLength. If the second argument is a + * Reader then the reader must contain the number of characters specified by scaleOrLength. If these conditions are not true the driver will + * generate a SQLServerException when the statement is executed. The default implementation will throw SQLFeatureNotSupportedException + * + * @param index + * the first column is 1, the second is 2, ... + * @param obj + * the new column value + * @param targetSqlType + * the SQL type to be sent to the database + * @param scale + * for an object of java.math.BigDecimal , this is the number of digits after the decimal point. For Java Object types InputStream and + * Reader, this is the length of the data in the stream or reader. For all other types, this value will be ignored. + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement.If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateObject(int index, + Object obj, + SQLType targetSqlType, + int scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * + * Updates the designated column with an Object value. The updater methods are used to update column values in the current row or the insert row. + * The updater methods do not update the underlying database; instead the updateRow or insertRow methods are called to update the database. If the + * second argument is an InputStream then the stream must contain the number of bytes specified by scaleOrLength. If the second argument is a + * Reader then the reader must contain the number of characters specified by scaleOrLength. If these conditions are not true the driver will + * generate a SQLServerException when the statement is executed. The default implementation will throw SQLFeatureNotSupportedException + * + * @param columnName + * the label for the column specified with the SQL AS clause. If the SQL AS clause was not specified, then the label is the name of the + * column + * @param obj + * the new column value + * @param targetSqlType + * the SQL type to be sent to the database + * @param scale + * for an object of java.math.BigDecimal , this is the number of digits after the decimal point. For Java Object types InputStream and + * Reader, this is the length of the data in the stream or reader. For all other types, this value will be ignored. + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement.If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateObject(String columnName, + Object obj, + SQLType targetSqlType, + int scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a boolean value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateBoolean(int index, + boolean x, + boolean forceEncrypt) throws SQLServerException; + /** + * Updates the designated column with a byte value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateByte(int index, + byte x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a short value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateShort(int index, + short x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with an int value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateInt(int index, + int x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a long value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateLong(int index, + long x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a float value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateFloat(int index, + float x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a double value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateDouble(int index, + double x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a money value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @throws SQLServerException + * when an error occurs + */ + public void updateMoney(int index, + BigDecimal x) throws SQLServerException; + + /** + * Updates the designated column with a money value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateMoney(int index, + BigDecimal x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a money value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param columnName + * the column name + * @param x + * the new column value + * @throws SQLServerException + * If any errors occur. + */ + public void updateMoney(String columnName, + BigDecimal x) throws SQLServerException; + + /** + * Updates the designated column with a money value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param columnName + * the column name + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateMoney(String columnName, + BigDecimal x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a smallmoney value. The updater methods are used to update column values in the current row or + * the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods + * are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @throws SQLServerException + * when an error occurs + */ + public void updateSmallMoney(int index, + BigDecimal x) throws SQLServerException; + + /** + * Updates the designated column with a smallmoney value. The updater methods are used to update column values in the current row or + * the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods + * are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateSmallMoney(int index, + BigDecimal x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a smallmoney value. The updater methods are used to update column values in the current row or + * the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods + * are called to update the database. + * + * @param columnName + * the column name + * @param x + * the new column value + * @throws SQLServerException + * If any errors occur. + */ + public void updateSmallMoney(String columnName, + BigDecimal x) throws SQLServerException; + + /** + * Updates the designated column with a smallmoney value. The updater methods are used to update column values in the current row or + * the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods + * are called to update the database. + * + * @param columnName + * the column name + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateSmallMoney(String columnName, + BigDecimal x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a java.math.BigDecimal value. The updater methods are used to update column values in the + * current row or the insert row. The updater methods do not update the underlying database; instead the updateRow or + * insertRow methods are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param precision + * the precision of the column + * @param scale + * the scale of the column + * @throws SQLServerException + * when an error occurs + */ + public void updateBigDecimal(int index, + BigDecimal x, + Integer precision, + Integer scale) throws SQLServerException; + + /** + * Updates the designated column with a java.math.BigDecimal value. The updater methods are used to update column values in the + * current row or the insert row. The updater methods do not update the underlying database; instead the updateRow or + * insertRow methods are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param precision + * the precision of the column + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateBigDecimal(int index, + BigDecimal x, + Integer precision, + Integer scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a String value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param stringValue + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateString(int columnIndex, + String stringValue, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a String value. It is intended for use when updating NCHAR,NVARCHAR + * and LONGNVARCHAR columns. The updater methods are used to update column values in the current row or the insert row. The updater + * methods do not update the underlying database; instead the updateRow or insertRow methods are called to update the + * database. + * + * @param columnIndex + * the first column is 1, the second 2, ... + * @param nString + * the value for the column to be updated + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateNString(int columnIndex, + String nString, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a String value. It is intended for use when updating NCHAR,NVARCHAR + * and LONGNVARCHAR columns. The updater methods are used to update column values in the current row or the insert row. The updater + * methods do not update the underlying database; instead the updateRow or insertRow methods are called to update the + * database. + * + * @param columnLabel + * the label for the column specified with the SQL AS clause. If the SQL AS clause was not specified, then the label is the name of the + * column + * @param nString + * the value for the column to be updated + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateNString(String columnLabel, + String nString, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a byte array value. The updater methods are used to update column values in the current row or + * the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods + * are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateBytes(int index, + byte x[], + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Date value. The updater methods are used to update column values in the current row + * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateDate(int index, + java.sql.Date x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Time value. The updater methods are used to update column values in the current row + * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param scale + * the scale of the column + * @throws SQLServerException + * when an error occurs + */ + public void updateTime(int index, + java.sql.Time x, + Integer scale) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Time value. The updater methods are used to update column values in the current row + * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateTime(int index, + java.sql.Time x, + Integer scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current + * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param scale + * the scale of the column + * @throws SQLServerException + * when an error occurs + */ + public void updateTimestamp(int index, + java.sql.Timestamp x, + int scale) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current + * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public 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 update column values in the current + * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @throws SQLServerException + * when an error occurs + */ + public void updateDateTime(int index, + java.sql.Timestamp x) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current + * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param scale + * the scale of the column + * @throws SQLServerException + * when an error occurs + */ + public void updateDateTime(int index, + java.sql.Timestamp x, + Integer scale) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current + * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public 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 update column values in the current + * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @throws SQLServerException + * when an error occurs + */ + public void updateSmallDateTime(int index, + java.sql.Timestamp x) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current + * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param scale + * the scale of the column + * @throws SQLServerException + * when an error occurs + */ + public void updateSmallDateTime(int index, + java.sql.Timestamp x, + Integer scale) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current + * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateSmallDateTime(int index, + java.sql.Timestamp x, + Integer scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the value of the column specified to the DateTimeOffset Class value, given a zero-based column ordinal. + * + * @param index + * The zero-based ordinal of a column. + * @param x + * A DateTimeOffset Class object. + * @param scale + * scale of the column + * @throws SQLServerException + * when an error occurs + */ + public 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. + * + * @param index + * The zero-based ordinal of a column. + * @param x + * A DateTimeOffset Class object. + * @param scale + * scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateDateTimeOffset(int index, + microsoft.sql.DateTimeOffset x, + Integer scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a String value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param index + * The zero-based ordinal of a column. + * @param x + * the new column value + * @throws SQLServerException + * when an error occurs + */ + public void updateUniqueIdentifier(int index, + String x) throws SQLServerException; + + /** + * Updates the designated column with a String value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param index + * The zero-based ordinal of a column. + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateUniqueIdentifier(int index, + String x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with an {@code Object} value. + * + * The updater methods are used to update column values in the current row or the insert row. The updater methods do not update the underlying + * database; instead the {@code updateRow} or {@code insertRow} methods are called to update the database. + * + * @param index + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param precision + * the precision of the column + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public 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 values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * when an error occurs + */ + public void updateBoolean(String columnName, + boolean x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a byte value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * + * @param columnName + * the name of the column + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateByte(String columnName, + byte x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a short value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateShort(String columnName, + short x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with an int value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateInt(String columnName, + int x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a long value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateLong(String columnName, + long x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a float value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateFloat(String columnName, + float x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a double value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateDouble(String columnName, + double x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.BigDecimal value. The updater methods are used to update column values in the + * current row or the insert row. The updater methods do not update the underlying database; instead the updateRow or + * insertRow methods are called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateBigDecimal(String columnName, + BigDecimal x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.BigDecimal value. The updater methods are used to update column values in the + * current row or the insert row. The updater methods do not update the underlying database; instead the updateRow or + * insertRow methods are called to update the database. + * + * @param columnName + * is the name of the column and Always Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set + * to false, the driver will not force encryption on parameters. + * @param x + * BigDecimal value + * @param precision + * the precision of the column + * @param scale + * the scale of the column + * @throws SQLServerException + * If any errors occur. + */ + public 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 update column values in the + * current row or the insert row. The updater methods do not update the underlying database; instead the updateRow or + * insertRow methods are called to update the database. + * + * @param columnName + * is the name of the column and Always Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set + * to false, the driver will not force encryption on parameters. + * @param x + * BigDecimal value + * @param precision + * the precision of the column + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateBigDecimal(String columnName, + BigDecimal x, + Integer precision, + Integer scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a String value. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateString(String columnName, + String x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a byte array value. + * + * The updater methods are used to update column values in the current row or the insert row. The updater methods do not update the underlying + * database; instead the updateRow or insertRow methods are called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateBytes(String columnName, + byte x[], + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Date value. The updater methods are used to update column values in the current row + * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateDate(String columnName, + java.sql.Date x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Time value. The updater methods are used to update column values in the current row + * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @param scale + * the scale of the column + * @throws SQLServerException + * If any errors occur. + */ + public void updateTime(String columnName, + java.sql.Time x, + int scale) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Time value. The updater methods are used to update column values in the current row + * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public 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 update column values in the current + * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @param scale + * the scale of the column + * @throws SQLServerException + * If any errors occur. + */ + public void updateTimestamp(String columnName, + java.sql.Timestamp x, + int scale) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current + * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateTimestamp(String columnName, + 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 update column values in the current + * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @throws SQLServerException + * If any errors occur. + */ + public void updateDateTime(String columnName, + java.sql.Timestamp x) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current + * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @param scale + * the scale of the column + * @throws SQLServerException + * If any errors occur. + */ + public void updateDateTime(String columnName, + java.sql.Timestamp x, + int scale) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current + * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateDateTime(String columnName, + 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 update column values in the current + * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @throws SQLServerException + * If any errors occur. + */ + public void updateSmallDateTime(String columnName, + java.sql.Timestamp x) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current + * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @param scale + * the scale of the column + * @throws SQLServerException + * If any errors occur. + */ + public void updateSmallDateTime(String columnName, + java.sql.Timestamp x, + int scale) throws SQLServerException; + + /** + * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current + * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow + * methods are called to update the database. + * + * @param columnName + * is the name of the column + * @param x + * the new column value + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateSmallDateTime(String columnName, + java.sql.Timestamp x, + int scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the value of the column specified to the DateTimeOffset Class value, given a column name. + * + * @param columnName + * The name of a column. + * @param x + * A DateTimeOffset Class object. + * @param scale + * the scale of the column + * @throws SQLServerException + * If any errors occur. + */ + public 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. + * + * @param columnName + * The name of a column. + * @param x + * A DateTimeOffset Class object. + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateDateTimeOffset(String columnName, + microsoft.sql.DateTimeOffset x, + int scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with a Stringvalue. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param columnName + * The name of a column. + * @param x + * the new column value + * @throws SQLServerException + * If any errors occur. + */ + public void updateUniqueIdentifier(String columnName, + String x) throws SQLServerException; + + /** + * Updates the designated column with a Stringvalue. The updater methods are used to update column values in the current row or the + * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are + * called to update the database. + * + * @param columnName + * The name of a column. + * @param x + * the new column value + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateUniqueIdentifier(String columnName, + String x, + boolean forceEncrypt) throws SQLServerException; + + /** + * Updates the designated column with an {@code Object} value. + * + * The updater methods are used to update column values in the current row or the insert row. The updater methods do not update the underlying + * database; instead the {@code updateRow} or {@code insertRow} methods are called to update the database. + * + * @param columnName + * The name of a column. + * @param x + * the new column value + * @param precision + * the precision of the column + * @param scale + * the scale of the column + * @throws SQLServerException + * If any errors occur. + */ + public void updateObject(String columnName, + Object x, + int precision, + int scale) throws SQLServerException; + + /** + * Updates the designated column with an {@code Object} value. + * + * The updater methods are used to update column values in the current row or the insert row. The updater methods do not update the underlying + * database; instead the {@code updateRow} or {@code insertRow} methods are called to update the database. + * + * @param columnName + * The name of a column. + * @param x + * the new column value + * @param precision + * the precision of the column + * @param scale + * the scale of the column + * @param forceEncrypt + * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always + * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force + * encryption on parameters. + * @throws SQLServerException + * If any errors occur. + */ + public void updateObject(String columnName, + Object x, + int precision, + int scale, + boolean forceEncrypt) throws SQLServerException; + + /** + * Exposes Data Classification information for the current ResultSet For SQL Servers that do not support Data Classification or results that do + * not fetch any classified columns, this data can be null + * + * @return SensitivityClassification + */ + public SensitivityClassification getSensitivityClassification(); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSet42.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSet42.java deleted file mode 100644 index 1f75ec99f..000000000 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSet42.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.jdbc; - -import java.sql.SQLType; - -/** - * This interface requires all the ResultSet methods including those are specific to JDBC 4.2 - * - */ -public interface ISQLServerResultSet42 extends ISQLServerResultSet { - - public void updateObject(int index, - Object obj, - SQLType targetSqlType) throws SQLServerException; - - public void updateObject(int index, - Object obj, - SQLType targetSqlType, - int scale) throws SQLServerException; - - /** - * Updates the designated column with an Object value. The updater methods are used to update column values in the current row or the insert row. - * The updater methods do not update the underlying database; instead the updateRow or insertRow methods are called to update the database. If the - * second argument is an InputStream then the stream must contain the number of bytes specified by scaleOrLength. If the second argument is a - * Reader then the reader must contain the number of characters specified by scaleOrLength. If these conditions are not true the driver will - * generate a SQLException when the statement is executed. The default implementation will throw SQLFeatureNotSupportedException - * - * @param index - * the first column is 1, the second is 2, ... - * @param obj - * the new column value - * @param targetSqlType - * the SQL type to be sent to the database - * @param scale - * for an object of java.math.BigDecimal , this is the number of digits after the decimal point. For Java Object types InputStream and - * Reader, this is the length of the data in the stream or reader. For all other types, this value will be ignored. - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement.If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ - public void updateObject(int index, - Object obj, - SQLType targetSqlType, - int scale, - boolean forceEncrypt) throws SQLServerException; - - public void updateObject(String columnName, - Object obj, - SQLType targetSqlType, - int scale) throws SQLServerException; - - /** - * - * Updates the designated column with an Object value. The updater methods are used to update column values in the current row or the insert row. - * The updater methods do not update the underlying database; instead the updateRow or insertRow methods are called to update the database. If the - * second argument is an InputStream then the stream must contain the number of bytes specified by scaleOrLength. If the second argument is a - * Reader then the reader must contain the number of characters specified by scaleOrLength. If these conditions are not true the driver will - * generate a SQLException when the statement is executed. The default implementation will throw SQLFeatureNotSupportedException - * - * @param columnName - * the label for the column specified with the SQL AS clause. If the SQL AS clause was not specified, then the label is the name of the - * column - * @param obj - * the new column value - * @param targetSqlType - * the SQL type to be sent to the database - * @param scale - * for an object of java.math.BigDecimal , this is the number of digits after the decimal point. For Java Object types InputStream and - * Reader, this is the length of the data in the stream or reader. For all other types, this value will be ignored. - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement.If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ - public void updateObject(String columnName, - Object obj, - SQLType targetSqlType, - int scale, - boolean forceEncrypt) throws SQLServerException; - - public void updateObject(String columnName, - Object obj, - SQLType targetSqlType) throws SQLServerException; -} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSetMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSetMetaData.java new file mode 100644 index 000000000..25f88d733 --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSetMetaData.java @@ -0,0 +1,29 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + +package com.microsoft.sqlserver.jdbc; + +import java.sql.ResultSetMetaData; + +/** + * This interface is implemented by {@link SQLServerResultSetMetaData} class. + */ +public interface ISQLServerResultSetMetaData extends ResultSetMetaData { + + /** + * Returns true if the column is a SQLServer SparseColumnSet + * + * @param column + * The column number + * @return true if a column in a result set is a sparse column set, otherwise false. + * @throws SQLServerException + * when an error occurs + */ + public boolean isSparseColumnSet(int column) throws SQLServerException; + +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerSavepoint.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerSavepoint.java new file mode 100644 index 000000000..4fd4dbb1a --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerSavepoint.java @@ -0,0 +1,39 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + +package com.microsoft.sqlserver.jdbc; + +import java.sql.Savepoint; + +/** + * This interface is implemented by {@link SQLServerSavepoint} class. + */ +public interface ISQLServerSavepoint extends Savepoint { + + /** + * Get the savepoint name + * + * @return the name of savepoint + */ + public String getSavepointName() throws SQLServerException; + + /** + * Get the savepoint label + * + * @return the label for Savepoint + */ + public String getLabel(); + + /** + * Checks if the savepoint label is null + * + * @return true is the savepoint is named. Otherwise, false. + */ + public boolean isNamed(); + +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerStatement.java index 518a74cb2..a1ba642a7 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerStatement.java @@ -1,39 +1,62 @@ -/* - * 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; - -public interface ISQLServerStatement extends java.sql.Statement { - /** - * Sets the response buffering mode for this SQLServerStatement object to case-insensitive String full or adaptive. - *

- * Response buffering controls the driver's buffering of responses from SQL Server. - *

- * Possible values are: - *

- * "full" - Fully buffer the response at execution time. - *

- * "adaptive" - Data Pipe adaptive buffering - * - * @param value - * A String that contains the response buffering mode. The valid mode can be one of the following case-insensitive Strings: full or - * adaptive. - * @throws SQLServerException - * If there are any errors in setting the response buffering mode. - */ - public void setResponseBuffering(String value) throws SQLServerException; - - /** - * Retrieves the response buffering mode for this SQLServerStatement object. - * - * @return A String that contains a lower-case full or adaptive. - * @throws SQLServerException - * If there are any errors in retrieving the response buffering mode. - */ - public String getResponseBuffering() throws SQLServerException; -} +/* + * 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; + +/** + * This interface is implemented by {@link SQLServerStatement} class. + */ +public interface ISQLServerStatement extends java.sql.Statement { + /** + * Sets the response buffering mode for this SQLServerStatement object to case-insensitive String full or adaptive. + *

+ * Response buffering controls the driver's buffering of responses from SQL Server. + *

+ * Possible values are: + *

+ * "full" - Fully buffer the response at execution time. + *

+ * "adaptive" - Data Pipe adaptive buffering + * + * @param value + * A String that contains the response buffering mode. The valid mode can be one of the following case-insensitive Strings: full or + * adaptive. + * @throws SQLServerException + * If there are any errors in setting the response buffering mode. + */ + public void setResponseBuffering(String value) throws SQLServerException; + + /** + * Retrieves the response buffering mode for this SQLServerStatement object. + * + * @return A String that contains a lower-case full or adaptive. + * @throws SQLServerException + * If there are any errors in retrieving the response buffering mode. + */ + public String getResponseBuffering() throws SQLServerException; + + /** + * Retrieves the cancelQueryTimeout property set on this SQLServerStatement object. + * + * @return cancelQueryTimeout Time duration in seconds. + * @throws SQLServerException + * if any error occurs + */ + public int getCancelQueryTimeout() throws SQLServerException; + + /** + * Sets the cancelQueryTimeout property on this SQLServerStatement object to cancel queryTimeout set on Connection or + * Statement level. + * + * @param seconds + * Time duration in seconds. + * @throws SQLServerException + * if any error occurs + */ + public void setCancelQueryTimeout(int seconds) throws SQLServerException; +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java index 1a0f02b51..6dcf58cf6 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java @@ -35,7 +35,12 @@ */ final class SQLCollation implements java.io.Serializable { - private final int info; // First 4 bytes of TDS collation. + /** + * + */ + private static final long serialVersionUID = 6748833280721312349L; + + private final int info; // First 4 bytes of TDS collation. private int langID() { return info & 0x0000FFFF; } private final int sortId; // 5th byte of TDS collation. private final Encoding encoding; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBlob.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBlob.java index f0d18eabb..3631e2374 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBlob.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBlob.java @@ -12,9 +12,7 @@ import java.io.Closeable; import java.io.IOException; import java.io.InputStream; -import java.sql.Blob; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicInteger; @@ -26,6 +24,9 @@ */ public final class SQLServerBlob extends SQLServerLob implements java.sql.Blob, java.io.Serializable { + /** + * Always refresh SerialVersionUID when prompted + */ private static final long serialVersionUID = -3526170228097889085L; // The value of the BLOB that this Blob object represents. @@ -38,15 +39,17 @@ public final class SQLServerBlob extends SQLServerLob implements java.sql.Blob, // Active streams which must be closed when the Blob is closed // // Initial size of the array is based on an assumption that a Blob object is - // typically used either for input or output, and then only once. The array size + // typically used either for input or output, and then only once. The array + // size // grows automatically if multiple streams are used. ArrayList activeStreams = new ArrayList<>(1); static private final Logger logger = Logger.getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerBlob"); - static private final AtomicInteger baseID = new AtomicInteger(0); // Unique id generator for each instance (used for logging). + // Unique id generator for each instance (use for logging). + static private final AtomicInteger baseID = new AtomicInteger(0); final private String traceID; - + final public String toString() { return traceID; } @@ -63,7 +66,7 @@ private static int nextInstanceID() { * the database connection this blob is implemented on * @param data * the BLOB's data - * @deprecated Use {@link SQLServerConnection#createBlob()} instead. + * @deprecated Use {@link SQLServerConnection#createBlob()} instead. */ @Deprecated public SQLServerBlob(SQLServerConnection connection, @@ -71,8 +74,10 @@ public SQLServerBlob(SQLServerConnection connection, traceID = " SQLServerBlob:" + nextInstanceID(); con = connection; - // Disallow Blobs with internal null values. We throw a NullPointerException here - // because the method signature of the public constructor does not permit a SQLException + // Disallow Blobs with internal null values. We throw a + // NullPointerException here + // because the method signature of the public constructor does not + // permit a SQLException // to be thrown. if (null == data) throw new NullPointerException(SQLServerException.getErrString("R_cantSetNull")); @@ -100,15 +105,11 @@ public SQLServerBlob(SQLServerConnection connection, logger.fine(toString() + " created by (null connection)"); } - /** - * Frees this Blob object and releases the resources that it holds. - *

- * After free() has been called, any attempt to invoke a method other than free() will result in a SQLException being thrown. If free() is called - * multiple times, the subsequent calls to free are treated as a no-op. - */ + @Override public void free() throws SQLException { if (!isClosed) { - // Close active streams, ignoring any errors, since nothing can be done with them after that point anyway. + // Close active streams, ignoring any errors, since nothing can be + // done with them after that point anyway. if (null != activeStreams) { for (Closeable stream : activeStreams) { try { @@ -123,7 +124,6 @@ public void free() throws SQLException { // Discard the value value = null; - isClosed = true; } } @@ -138,9 +138,9 @@ private void checkClosed() throws SQLServerException { } } + @Override public InputStream getBinaryStream() throws SQLException { checkClosed(); - if (null == value && !activeStreams.isEmpty()) { InputStream stream = (InputStream) activeStreams.get(0); try { @@ -159,10 +159,11 @@ public InputStream getBinaryStream() throws SQLException { } } + @Override public InputStream getBinaryStream(long pos, long length) throws SQLException { - // Not implemented - partial materialization - throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + SQLServerException.throwFeatureNotSupportedException(); + return null; } private InputStream getBinaryStreamInternal(int pos, @@ -177,19 +178,7 @@ private InputStream getBinaryStreamInternal(int pos, return getterStream; } - /** - * Retrieves all or part of the BLOB value that this Blob object represents, as an array of bytes. This byte array contains up to length - * consecutive bytes starting at position pos. - * - * @param pos - * - the ordinal position of the first byte in the BLOB value to be extracted; the first byte is at position 1 - * @param length - * - the number of consecutive bytes to be copied; the value for length must be 0 or greater - * @return a byte array containing up to length consecutive bytes from the BLOB value designated by this Blob object, starting with the byte at - * position pos - * @throws SQLException - * - if there is an error accessing the BLOB value; if pos is less than 1 or length is less than 0 - */ + @Override public byte[] getBytes(long pos, int length) throws SQLException { checkClosed(); @@ -223,34 +212,26 @@ public byte[] getBytes(long pos, return bTemp; } - /** - * Return the length of the BLOB - * - * @throws SQLException - * when an error occurs - * @return the data length - */ + @Override public long length() throws SQLException { checkClosed(); if (value == null && activeStreams.get(0) instanceof PLPInputStream) { - return (long)((PLPInputStream)activeStreams.get(0)).payloadLength; + return (long) ((PLPInputStream) activeStreams.get(0)).payloadLength; } getBytesFromStream(); return value.length; } - - /** - * Function for the result set to maintain blobs it has created - * @throws SQLException - */ + + @Override void fillFromStream() throws SQLException { - if(!isClosed) { - getBytesFromStream(); - } + if (!isClosed) { + getBytesFromStream(); + } } - + /** * Converts stream to byte[] + * * @throws SQLServerException */ private void getBytesFromStream() throws SQLServerException { @@ -266,21 +247,11 @@ private void getBytesFromStream() throws SQLServerException { } } - /** - * Retrieves the byte position in the BLOB value designated by this Blob object at which pattern begins. The search begins at position start. - * - * @param pattern - * - the Blob object designating the BLOB value for which to search - * @param start - * - the position in the BLOB value at which to begin searching; the first position is 1 - * @return the postion at which the pattern begins, else -1 - * @throws SQLException - * - if there is an error accessing the BLOB value or if start is less than 1 - */ - public long position(Blob pattern, + @Override + public long position(java.sql.Blob pattern, long start) throws SQLException { checkClosed(); - + getBytesFromStream(); if (start < 1) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPositionIndex")); @@ -294,18 +265,7 @@ public long position(Blob pattern, return position(pattern.getBytes((long) 1, (int) pattern.length()), start); } - /** - * Retrieves the byte position at which the specified byte array pattern begins within the BLOB value that this Blob object represents. The search - * for pattern begins at position start. - * - * @param bPattern - * - the byte array for which to search - * @param start - * - the position at which to begin searching; the first position is 1 - * @return the position at which the pattern appears, else -1 - * @throws SQLException - * - if there is an error accessing the BLOB or if start is less than 1 - */ + @Override public long position(byte[] bPattern, long start) throws SQLException { checkClosed(); @@ -316,7 +276,8 @@ public long position(byte[] bPattern, SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } - // Back compat: Handle null search string as not found rather than throw an exception. + // Back compat: Handle null search string as not found rather than throw + // an exception. // JDBC spec doesn't describe the behavior for a null pattern. if (null == bPattern) return -1; @@ -342,20 +303,11 @@ public long position(byte[] bPattern, return -1; } - /* JDBC 3.0 methods */ - - /** - * Truncate a BLOB - * - * @param len - * the new length for the BLOB - * @throws SQLException - * when an error occurs - */ + @Override public void truncate(long len) throws SQLException { checkClosed(); getBytesFromStream(); - + if (len < 0) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidLength")); Object[] msgArgs = {len}; @@ -369,15 +321,7 @@ public void truncate(long len) throws SQLException { } } - /** - * Retrieves a stream that can be used to write to the BLOB value that this Blob object represents - * - * @param pos - * - the position in the BLOB value at which to start writing; the first position is 1 - * @return a java.io.OutputStream object to which data can be written - * @throws SQLException - * - if there is an error accessing the BLOB value or if pos is less than 1 - */ + @Override public java.io.OutputStream setBinaryStream(long pos) throws SQLException { checkClosed(); @@ -389,21 +333,11 @@ public java.io.OutputStream setBinaryStream(long pos) throws SQLException { return new SQLServerBlobOutputStream(this, pos); } - /** - * Writes the given array of bytes into the Blob starting at position pos, and returns the number of bytes written. - * - * @param pos - * the position (1 based) in the Blob object at which to start writing the data. - * @param bytes - * the array of bytes to be written into the Blob. - * @throws SQLException - * if there is an error accessing the BLOB value. - * @return the number of bytes written. - */ + @Override public int setBytes(long pos, byte[] bytes) throws SQLException { checkClosed(); - + getBytesFromStream(); if (null == bytes) SQLServerException.makeFromDriverError(con, null, SQLServerException.getErrString("R_cantSetNull"), null, true); @@ -411,26 +345,7 @@ public int setBytes(long pos, return setBytes(pos, bytes, 0, bytes.length); } - /** - * Writes all or part of the given byte array to the BLOB value that this Blob object represents and returns the number of bytes written. Writing - * starts at position pos in the BLOB value; len bytes from the given byte wrray are written. The array of bytes will overwrite the existing bytes - * in the Blob object starting at the position pos. If the end of the Blob value is reached while writing the array bytes, then the length of the - * Blob value will be increased to accomodate the extra bytes. - * - * SQL Server behavior: If the value specified for pos is greater than the length+1 of the BLOB value then a SQLException is thrown. - * - * @param pos - * - the position in the BLOB object at which to start writing; the first position is 1 - * @param bytes - * - the array of bytes to be written to this BLOB object. - * @param offset - * - the offset (0-based) into the array bytes at which to start reading the bytes to set - * @param len - * - the number of bytes to be written to the BLOB value from the array of bytes bytes - * @return the number of bytes written. - * @throws SQLException - * - if there is an error accessing the BLOB value or if pos is less than 1 - */ + @Override public int setBytes(long pos, byte[] bytes, int offset, @@ -469,7 +384,8 @@ public int setBytes(long pos, // Overwrite past end of value case. if (len >= value.length - pos) { - // Make sure the new value length wouldn't exceed the maximum allowed + // Make sure the new value length wouldn't exceed the maximum + // allowed DataTypes.getCheckedLength(con, JDBCType.BLOB, pos + len, false); assert pos + len <= Integer.MAX_VALUE; @@ -506,14 +422,14 @@ final class SQLServerBlobOutputStream extends java.io.OutputStream { this.currentPos = startPos; } - // java.io.OutputStream interface methods. - + @Override public void write(byte[] b) throws IOException { if (null == b) return; write(b, 0, b.length); } + @Override public void write(byte[] b, int off, int len) throws IOException { @@ -529,6 +445,7 @@ public void write(byte[] b, } } + @Override public void write(int b) throws java.io.IOException { byte[] bTemp = new byte[1]; bTemp[0] = (byte) (b & 0xFF); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java index 3bd33db7d..8ded138ac 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java @@ -21,403 +21,462 @@ import java.util.List; import java.util.Map.Entry; -import com.microsoft.sqlserver.jdbc.SQLServerBulkCommon.ColumnMetadata; - import java.util.Set; /** - * A simple implementation of the ISQLServerBulkRecord interface that can be used to read in the basic Java data types from an ArrayList of Parameters + * A simple implementation of the ISQLServerBulkRecord interface that can be + * used to read in the basic Java data types from an ArrayList of Parameters * that were provided by pstmt/cstmt. */ -public class SQLServerBulkBatchInsertRecord extends SQLServerBulkCommon implements ISQLServerBulkRecord { - - private List batchParam; - private int batchParamIndex = -1; - private List columnList; - private List valueList; - - /* - * Class name for logging. - */ - private static final String loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkBatchInsertRecord"; - - /* - * Logger - */ - private static final java.util.logging.Logger loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); - - public SQLServerBulkBatchInsertRecord(ArrayList batchParam, - ArrayList columnList, - ArrayList valueList, - String encoding) throws SQLServerException { - loggerExternal.entering(loggerClassName, "SQLServerBulkBatchInsertRecord", new Object[] {batchParam, encoding}); - - if (null == batchParam) { - throwInvalidArgument("batchParam"); - } - - if (null == valueList) { - throwInvalidArgument("valueList"); - } - - this.batchParam = batchParam; - this.columnList = columnList; - this.valueList = valueList; - columnMetadata = new HashMap<>(); - - loggerExternal.exiting(loggerClassName, "SQLServerBulkBatchInsertRecord"); - } - - public DateTimeFormatter getColumnDateTimeFormatter(int column) { - return columnMetadata.get(column).dateTimeFormatter; - } - - @Override - public Set getColumnOrdinals() { - return columnMetadata.keySet(); - } - - @Override - public String getColumnName(int column) { - return columnMetadata.get(column).columnName; - } - - @Override - public int getColumnType(int column) { - return columnMetadata.get(column).columnType; - } - - @Override - public int getPrecision(int column) { - return columnMetadata.get(column).precision; - } - - @Override - public int getScale(int column) { - return columnMetadata.get(column).scale; - } - - @Override - public boolean isAutoIncrement(int column) { - return false; - } - - private Object convertValue(ColumnMetadata cm, - Object data) throws SQLServerException { - switch (cm.columnType) { - case Types.INTEGER: { - // Formatter to remove the decimal part as SQL Server floors the decimal in integer types - DecimalFormat decimalFormatter = new DecimalFormat("#"); - decimalFormatter.setRoundingMode(RoundingMode.DOWN); - String formatedfInput = decimalFormatter.format(Double.parseDouble(data.toString())); - return Integer.valueOf(formatedfInput); - } - - case Types.TINYINT: - case Types.SMALLINT: { - // Formatter to remove the decimal part as SQL Server floors the decimal in integer types - DecimalFormat decimalFormatter = new DecimalFormat("#"); - decimalFormatter.setRoundingMode(RoundingMode.DOWN); - String formatedfInput = decimalFormatter.format(Double.parseDouble(data.toString())); - return Short.valueOf(formatedfInput); - } - - case Types.BIGINT: { - BigDecimal bd = new BigDecimal(data.toString().trim()); - try { - return bd.setScale(0, RoundingMode.DOWN).longValueExact(); - } - catch (ArithmeticException ex) { - String value = "'" + data + "'"; - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_errorConvertingValue")); - throw new SQLServerException(form.format(new Object[] {value, JDBCType.of(cm.columnType)}), null, 0, ex); - } - } - - case Types.DECIMAL: - case Types.NUMERIC: { - BigDecimal bd = new BigDecimal(data.toString().trim()); - return bd.setScale(cm.scale, RoundingMode.HALF_UP); - } - - case Types.BIT: { - // "true" => 1, "false" => 0 - // Any non-zero value (integer/double) => 1, 0/0.0 => 0 - try { - return (0 == Double.parseDouble(data.toString())) ? Boolean.FALSE : Boolean.TRUE; - } - catch (NumberFormatException e) { - return Boolean.parseBoolean(data.toString()); - } - } - - case Types.REAL: { - return Float.parseFloat(data.toString()); - } - - case Types.DOUBLE: { - return Double.parseDouble(data.toString()); - } - - case Types.BINARY: - case Types.VARBINARY: - case Types.LONGVARBINARY: - case Types.BLOB: { - // Strip off 0x if present. - String binData = data.toString().trim(); - if (binData.startsWith("0x") || binData.startsWith("0X")) { - return binData.substring(2); - } - else { - return binData; - } - } - - case java.sql.Types.TIME_WITH_TIMEZONE: - { - OffsetTime offsetTimeValue; - - // The per-column DateTimeFormatter gets priority. - if (null != cm.dateTimeFormatter) - offsetTimeValue = OffsetTime.parse(data.toString(), cm.dateTimeFormatter); - else if (timeFormatter != null) - offsetTimeValue = OffsetTime.parse(data.toString(), timeFormatter); - else - offsetTimeValue = OffsetTime.parse(data.toString()); - - return offsetTimeValue; - } - - case java.sql.Types.TIMESTAMP_WITH_TIMEZONE: - { - OffsetDateTime offsetDateTimeValue; - - // The per-column DateTimeFormatter gets priority. - if (null != cm.dateTimeFormatter) - offsetDateTimeValue = OffsetDateTime.parse(data.toString(), cm.dateTimeFormatter); - else if (dateTimeFormatter != null) - offsetDateTimeValue = OffsetDateTime.parse(data.toString(), dateTimeFormatter); - else - offsetDateTimeValue = OffsetDateTime.parse(data.toString()); - - return offsetDateTimeValue; - } - - case Types.NULL: { - return null; - } - - case Types.DATE: - case Types.CHAR: - case Types.NCHAR: - case Types.VARCHAR: - case Types.NVARCHAR: - case Types.LONGVARCHAR: - case Types.LONGNVARCHAR: - case Types.CLOB: - default: { - // The string is copied as is. - return data; - } - } - } - - private String removeSingleQuote(String s) { - int len = s.length(); - return (s.charAt(0) == '\'' && s.charAt(len - 1) == '\'') ? s.substring(1, len - 1) : s; - } - - @Override - public Object[] getRowData() throws SQLServerException { - Object[] data = new Object[columnMetadata.size()]; - int valueIndex = 0; - String valueData; - Object rowData; - int columnListIndex = 0; - - // check if the size of the list of values = size of the list of columns (which is optional) - if (null != columnList && columnList.size() != valueList.size()) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_DataSchemaMismatch")); - Object[] msgArgs = {}; - throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); - } - - for (Entry pair : columnMetadata.entrySet()) { - int index = pair.getKey() - 1; - - // To explain what each variable represents: - // columnMetadata = map containing the ENTIRE list of columns in the table. - // columnList = the *optional* list of columns the user can provide. For example, the (c1, c3) part of this query: INSERT into t1 (c1, c3) values (?, ?) - // valueList = the *mandatory* list of columns the user needs provide. This is the (?, ?) part of the previous query. The size of this valueList will always equal the number of - // the entire columns in the table IF columnList has NOT been provided. If columnList HAS been provided, then this valueList may be smaller than the list of all columns (which is columnMetadata). - - // case when the user has not provided the optional list of column names. - if (null == columnList || columnList.size() == 0) { - valueData = valueList.get(index); - // if the user has provided a wildcard for this column, fetch the set value from the batchParam. - if (valueData.equalsIgnoreCase("?")) { - rowData = batchParam.get(batchParamIndex)[valueIndex++].getSetterValue(); - } - else if (valueData.equalsIgnoreCase("null")) { - rowData = null; - } - // if the user has provided a hardcoded value for this column, rowData is simply set to the hardcoded value. - else { - rowData = removeSingleQuote(valueData); - } - } - // case when the user has provided the optional list of column names. - else { - // columnListIndex is a separate counter we need to keep track of for each time we've processed a column - // that the user provided. - // for example, if the user provided an optional columnList of (c1, c3, c5, c7) in a table that has 8 columns (c1~c8), - // then the columnListIndex would increment only when we're dealing with the four columns inside columnMetadata. - // compare the list of the optional list of column names to the table's metadata, and match each other, so we assign the correct value to each column. - if (columnList.size() > columnListIndex && columnList.get(columnListIndex).equalsIgnoreCase(columnMetadata.get(index + 1).columnName)) { - valueData = valueList.get(columnListIndex); - if (valueData.equalsIgnoreCase("?")) { - rowData = batchParam.get(batchParamIndex)[valueIndex++].getSetterValue(); - } - else if (valueData.equalsIgnoreCase("null")) { - rowData = null; - } - else { - rowData = removeSingleQuote(valueData); - } - columnListIndex++; - } - else { - rowData = null; - } - } - - try { - if (null == rowData) { - data[index] = null; - continue; - } else if (0 == rowData.toString().length()) { - data[index] = ""; - continue; - } - data[index] = convertValue(pair.getValue(), rowData); - } - catch (IllegalArgumentException e) { - String value = "'" + rowData + "'"; - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_errorConvertingValue")); - throw new SQLServerException(form.format(new Object[] {value, JDBCType.of(pair.getValue().columnType)}), null, 0, e); - } - catch (ArrayIndexOutOfBoundsException e) { - throw new SQLServerException(SQLServerException.getErrString("R_DataSchemaMismatch"), e); - } - } - return data; - } - - @Override - void addColumnMetadataInternal(int positionInSource, - String name, - int jdbcType, - int precision, - int scale, - DateTimeFormatter dateTimeFormatter) throws SQLServerException { - loggerExternal.entering(loggerClassName, "addColumnMetadata", new Object[] {positionInSource, name, jdbcType, precision, scale}); - - String colName = ""; - - if (0 >= positionInSource) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumnOrdinal")); - Object[] msgArgs = {positionInSource}; - throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); - } - - if (null != name) - colName = name.trim(); - else if ((null != columnNames) && (columnNames.length >= positionInSource)) - colName = columnNames[positionInSource - 1]; - - if ((null != columnNames) && (positionInSource > columnNames.length)) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumn")); - Object[] msgArgs = {positionInSource}; - throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); - } - - checkDuplicateColumnName(positionInSource, name); - switch (jdbcType) { - /* - * SQL Server supports numerous string literal formats for temporal types, hence sending them as varchar with approximate - * precision(length) needed to send supported string literals. string literal formats supported by temporal types are available in MSDN - * page on data types. - */ - case java.sql.Types.DATE: - case java.sql.Types.TIME: - case java.sql.Types.TIMESTAMP: - case microsoft.sql.Types.DATETIMEOFFSET: - columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, precision, scale, dateTimeFormatter)); - break; - - // Redirect SQLXML as LONGNVARCHAR - // SQLXML is not valid type in TDS - case java.sql.Types.SQLXML: - columnMetadata.put(positionInSource, new ColumnMetadata(colName, java.sql.Types.LONGNVARCHAR, precision, scale, dateTimeFormatter)); - break; - - // Redirecting Float as Double based on data type mapping - // https://msdn.microsoft.com/en-us/library/ms378878%28v=sql.110%29.aspx - case java.sql.Types.FLOAT: - columnMetadata.put(positionInSource, new ColumnMetadata(colName, java.sql.Types.DOUBLE, precision, scale, dateTimeFormatter)); - break; - - // redirecting BOOLEAN as BIT - case java.sql.Types.BOOLEAN: - columnMetadata.put(positionInSource, new ColumnMetadata(colName, java.sql.Types.BIT, precision, scale, dateTimeFormatter)); - break; - - default: - columnMetadata.put(positionInSource, new ColumnMetadata(colName, jdbcType, precision, scale, dateTimeFormatter)); - } - - loggerExternal.exiting(loggerClassName, "addColumnMetadata"); - } - - @Override - public void setTimestampWithTimezoneFormat(String dateTimeFormat) { - loggerExternal.entering(loggerClassName, "setTimestampWithTimezoneFormat", dateTimeFormat); - - super.setTimestampWithTimezoneFormat(dateTimeFormat); - - loggerExternal.exiting(loggerClassName, "setTimestampWithTimezoneFormat"); - } - - @Override - public void setTimestampWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { - loggerExternal.entering(loggerClassName, "setTimestampWithTimezoneFormat", new Object[] {dateTimeFormatter}); - - super.setTimestampWithTimezoneFormat(dateTimeFormatter); - - loggerExternal.exiting(loggerClassName, "setTimestampWithTimezoneFormat"); - } - - @Override - public void setTimeWithTimezoneFormat(String timeFormat) { - loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", timeFormat); - - super.setTimeWithTimezoneFormat(timeFormat); - - loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); - } - - @Override - public void setTimeWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { - loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", new Object[] {dateTimeFormatter}); - - super.setTimeWithTimezoneFormat(dateTimeFormatter); - - loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); - } - - @Override - public boolean next() throws SQLServerException { - batchParamIndex++; - return batchParamIndex < batchParam.size(); - } +public class SQLServerBulkBatchInsertRecord extends SQLServerBulkCommon { + + private List batchParam; + private int batchParamIndex = -1; + private List columnList; + private List valueList; + + /* + * Class name for logging. + */ + private static final String loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkBatchInsertRecord"; + + /* + * Logger + */ + private static final java.util.logging.Logger loggerExternal = java.util.logging.Logger + .getLogger(loggerClassName); + + public SQLServerBulkBatchInsertRecord(ArrayList batchParam, + ArrayList columnList, ArrayList valueList, + String encoding) throws SQLServerException { + loggerExternal.entering(loggerClassName, + "SQLServerBulkBatchInsertRecord", + new Object[]{batchParam, encoding}); + + if (null == batchParam) { + throwInvalidArgument("batchParam"); + } + + if (null == valueList) { + throwInvalidArgument("valueList"); + } + + this.batchParam = batchParam; + this.columnList = columnList; + this.valueList = valueList; + columnMetadata = new HashMap<>(); + + loggerExternal.exiting(loggerClassName, + "SQLServerBulkBatchInsertRecord"); + } + + @Override + public DateTimeFormatter getColumnDateTimeFormatter(int column) { + return columnMetadata.get(column).dateTimeFormatter; + } + + @Override + public Set getColumnOrdinals() { + return columnMetadata.keySet(); + } + + @Override + public String getColumnName(int column) { + return columnMetadata.get(column).columnName; + } + + @Override + public int getColumnType(int column) { + return columnMetadata.get(column).columnType; + } + + @Override + public int getPrecision(int column) { + return columnMetadata.get(column).precision; + } + + @Override + public int getScale(int column) { + return columnMetadata.get(column).scale; + } + + @Override + public boolean isAutoIncrement(int column) { + return false; + } + + private Object convertValue(ColumnMetadata cm, Object data) + throws SQLServerException { + switch (cm.columnType) { + case Types.INTEGER : { + // Formatter to remove the decimal part as SQL Server floors the + // decimal in integer types + DecimalFormat decimalFormatter = new DecimalFormat("#"); + decimalFormatter.setRoundingMode(RoundingMode.DOWN); + String formatedfInput = decimalFormatter + .format(Double.parseDouble(data.toString())); + return Integer.valueOf(formatedfInput); + } + + case Types.TINYINT : + case Types.SMALLINT : { + // Formatter to remove the decimal part as SQL Server floors the + // decimal in integer types + DecimalFormat decimalFormatter = new DecimalFormat("#"); + decimalFormatter.setRoundingMode(RoundingMode.DOWN); + String formatedfInput = decimalFormatter + .format(Double.parseDouble(data.toString())); + return Short.valueOf(formatedfInput); + } + + case Types.BIGINT : { + BigDecimal bd = new BigDecimal(data.toString().trim()); + try { + return bd.setScale(0, RoundingMode.DOWN).longValueExact(); + } catch (ArithmeticException ex) { + String value = "'" + data + "'"; + MessageFormat form = new MessageFormat(SQLServerException + .getErrString("R_errorConvertingValue")); + throw new SQLServerException(form.format( + new Object[]{value, JDBCType.of(cm.columnType)}), + null, 0, ex); + } + } + + case Types.DECIMAL : + case Types.NUMERIC : { + BigDecimal bd = new BigDecimal(data.toString().trim()); + return bd.setScale(cm.scale, RoundingMode.HALF_UP); + } + + case Types.BIT : { + // "true" => 1, "false" => 0 + // Any non-zero value (integer/double) => 1, 0/0.0 => 0 + try { + return (0 == Double.parseDouble(data.toString())) + ? Boolean.FALSE + : Boolean.TRUE; + } catch (NumberFormatException e) { + return Boolean.parseBoolean(data.toString()); + } + } + + case Types.REAL : { + return Float.parseFloat(data.toString()); + } + + case Types.DOUBLE : { + return Double.parseDouble(data.toString()); + } + + case Types.BINARY : + case Types.VARBINARY : + case Types.LONGVARBINARY : + case Types.BLOB : { + // Strip off 0x if present. + String binData = data.toString().trim(); + if (binData.startsWith("0x") || binData.startsWith("0X")) { + return binData.substring(2); + } else { + return binData; + } + } + + case java.sql.Types.TIME_WITH_TIMEZONE : { + OffsetTime offsetTimeValue; + + // The per-column DateTimeFormatter gets priority. + if (null != cm.dateTimeFormatter) + offsetTimeValue = OffsetTime.parse(data.toString(), + cm.dateTimeFormatter); + else if (timeFormatter != null) + offsetTimeValue = OffsetTime.parse(data.toString(), + timeFormatter); + else + offsetTimeValue = OffsetTime.parse(data.toString()); + + return offsetTimeValue; + } + + case java.sql.Types.TIMESTAMP_WITH_TIMEZONE : { + OffsetDateTime offsetDateTimeValue; + + // The per-column DateTimeFormatter gets priority. + if (null != cm.dateTimeFormatter) + offsetDateTimeValue = OffsetDateTime.parse(data.toString(), + cm.dateTimeFormatter); + else if (dateTimeFormatter != null) + offsetDateTimeValue = OffsetDateTime.parse(data.toString(), + dateTimeFormatter); + else + offsetDateTimeValue = OffsetDateTime.parse(data.toString()); + + return offsetDateTimeValue; + } + + case Types.NULL : { + return null; + } + + case Types.DATE : + case Types.CHAR : + case Types.NCHAR : + case Types.VARCHAR : + case Types.NVARCHAR : + case Types.LONGVARCHAR : + case Types.LONGNVARCHAR : + case Types.CLOB : + default : { + // The string is copied as is. + return data; + } + } + } + + private String removeSingleQuote(String s) { + int len = s.length(); + return (s.charAt(0) == '\'' && s.charAt(len - 1) == '\'') + ? s.substring(1, len - 1) + : s; + } + + @Override + public Object[] getRowData() throws SQLServerException { + Object[] data = new Object[columnMetadata.size()]; + int valueIndex = 0; + String valueData; + Object rowData; + int columnListIndex = 0; + + // check if the size of the list of values = size of the list of columns + // (which is optional) + if (null != columnList && columnList.size() != valueList.size()) { + MessageFormat form = new MessageFormat( + SQLServerException.getErrString("R_DataSchemaMismatch")); + Object[] msgArgs = {}; + throw new SQLServerException(form.format(msgArgs), + SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); + } + + for (Entry pair : columnMetadata.entrySet()) { + int index = pair.getKey() - 1; + + // To explain what each variable represents: + // columnMetadata = map containing the ENTIRE list of columns in the + // table. + // columnList = the *optional* list of columns the user can provide. + // For example, the (c1, c3) part of this query: INSERT into t1 (c1, + // c3) values (?, ?) + // valueList = the *mandatory* list of columns the user needs + // provide. This is the (?, ?) part of the previous query. The size + // of this valueList will always equal the number of + // the entire columns in the table IF columnList has NOT been + // provided. If columnList HAS been provided, then this valueList + // may be smaller than the list of all columns (which is + // columnMetadata). + + // case when the user has not provided the optional list of column + // names. + if (null == columnList || columnList.size() == 0) { + valueData = valueList.get(index); + // if the user has provided a wildcard for this column, fetch + // the set value from the batchParam. + if (valueData.equalsIgnoreCase("?")) { + rowData = batchParam.get(batchParamIndex)[valueIndex++] + .getSetterValue(); + } else if (valueData.equalsIgnoreCase("null")) { + rowData = null; + } + // if the user has provided a hardcoded value for this column, + // rowData is simply set to the hardcoded value. + else { + rowData = removeSingleQuote(valueData); + } + } + // case when the user has provided the optional list of column + // names. + else { + // columnListIndex is a separate counter we need to keep track + // of for each time we've processed a column + // that the user provided. + // for example, if the user provided an optional columnList of + // (c1, c3, c5, c7) in a table that has 8 columns (c1~c8), + // then the columnListIndex would increment only when we're + // dealing with the four columns inside columnMetadata. + // compare the list of the optional list of column names to the + // table's metadata, and match each other, so we assign the + // correct value to each column. + if (columnList.size() > columnListIndex + && columnList.get(columnListIndex).equalsIgnoreCase( + columnMetadata.get(index + 1).columnName)) { + valueData = valueList.get(columnListIndex); + if (valueData.equalsIgnoreCase("?")) { + rowData = batchParam.get(batchParamIndex)[valueIndex++] + .getSetterValue(); + } else if (valueData.equalsIgnoreCase("null")) { + rowData = null; + } else { + rowData = removeSingleQuote(valueData); + } + columnListIndex++; + } else { + rowData = null; + } + } + + try { + if (null == rowData) { + data[index] = null; + continue; + } else if (0 == rowData.toString().length()) { + data[index] = ""; + continue; + } + data[index] = convertValue(pair.getValue(), rowData); + } catch (IllegalArgumentException e) { + String value = "'" + rowData + "'"; + MessageFormat form = new MessageFormat(SQLServerException + .getErrString("R_errorConvertingValue")); + throw new SQLServerException( + form.format(new Object[]{value, + JDBCType.of(pair.getValue().columnType)}), + null, 0, e); + } catch (ArrayIndexOutOfBoundsException e) { + throw new SQLServerException( + SQLServerException.getErrString("R_DataSchemaMismatch"), + e); + } + } + return data; + } + + @Override + void addColumnMetadataInternal(int positionInSource, String name, + int jdbcType, int precision, int scale, + DateTimeFormatter dateTimeFormatter) throws SQLServerException { + loggerExternal.entering(loggerClassName, "addColumnMetadata", + new Object[]{positionInSource, name, jdbcType, precision, + scale}); + + String colName = ""; + + if (0 >= positionInSource) { + MessageFormat form = new MessageFormat( + SQLServerException.getErrString("R_invalidColumnOrdinal")); + Object[] msgArgs = {positionInSource}; + throw new SQLServerException(form.format(msgArgs), + SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); + } + + if (null != name) + colName = name.trim(); + else if ((null != columnNames) + && (columnNames.length >= positionInSource)) + colName = columnNames[positionInSource - 1]; + + if ((null != columnNames) && (positionInSource > columnNames.length)) { + MessageFormat form = new MessageFormat( + SQLServerException.getErrString("R_invalidColumn")); + Object[] msgArgs = {positionInSource}; + throw new SQLServerException(form.format(msgArgs), + SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null); + } + + checkDuplicateColumnName(positionInSource, name); + switch (jdbcType) { + /* + * SQL Server supports numerous string literal formats for temporal + * types, hence sending them as varchar with approximate + * precision(length) needed to send supported string literals. + * string literal formats supported by temporal types are available + * in MSDN page on data types. + */ + case java.sql.Types.DATE : + case java.sql.Types.TIME : + case java.sql.Types.TIMESTAMP : + case microsoft.sql.Types.DATETIMEOFFSET : + columnMetadata.put(positionInSource, new ColumnMetadata(colName, + jdbcType, precision, scale, dateTimeFormatter)); + break; + + // Redirect SQLXML as LONGNVARCHAR + // SQLXML is not valid type in TDS + case java.sql.Types.SQLXML : + columnMetadata.put(positionInSource, + new ColumnMetadata(colName, java.sql.Types.LONGNVARCHAR, + precision, scale, dateTimeFormatter)); + break; + + // Redirecting Float as Double based on data type mapping + // https://msdn.microsoft.com/en-us/library/ms378878%28v=sql.110%29.aspx + case java.sql.Types.FLOAT : + columnMetadata.put(positionInSource, + new ColumnMetadata(colName, java.sql.Types.DOUBLE, + precision, scale, dateTimeFormatter)); + break; + + // redirecting BOOLEAN as BIT + case java.sql.Types.BOOLEAN : + columnMetadata.put(positionInSource, + new ColumnMetadata(colName, java.sql.Types.BIT, + precision, scale, dateTimeFormatter)); + break; + + default : + columnMetadata.put(positionInSource, new ColumnMetadata(colName, + jdbcType, precision, scale, dateTimeFormatter)); + } + + loggerExternal.exiting(loggerClassName, "addColumnMetadata"); + } + + @Override + public void setTimestampWithTimezoneFormat(String dateTimeFormat) { + loggerExternal.entering(loggerClassName, + "setTimestampWithTimezoneFormat", dateTimeFormat); + + super.setTimestampWithTimezoneFormat(dateTimeFormat); + + loggerExternal.exiting(loggerClassName, + "setTimestampWithTimezoneFormat"); + } + + @Override + public void setTimestampWithTimezoneFormat( + DateTimeFormatter dateTimeFormatter) { + loggerExternal.entering(loggerClassName, + "setTimestampWithTimezoneFormat", + new Object[]{dateTimeFormatter}); + + super.setTimestampWithTimezoneFormat(dateTimeFormatter); + + loggerExternal.exiting(loggerClassName, + "setTimestampWithTimezoneFormat"); + } + + @Override + public void setTimeWithTimezoneFormat(String timeFormat) { + loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", + timeFormat); + + super.setTimeWithTimezoneFormat(timeFormat); + + loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); + } + + @Override + public void setTimeWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { + loggerExternal.entering(loggerClassName, "setTimeWithTimezoneFormat", + new Object[]{dateTimeFormatter}); + + super.setTimeWithTimezoneFormat(dateTimeFormatter); + + loggerExternal.exiting(loggerClassName, "setTimeWithTimezoneFormat"); + } + + @Override + public boolean next() throws SQLServerException { + batchParamIndex++; + return batchParamIndex < batchParam.size(); + } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java index 019046ab6..31e101463 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java @@ -25,15 +25,13 @@ import java.util.HashMap; import java.util.Map.Entry; -import com.microsoft.sqlserver.jdbc.SQLServerBulkCommon.ColumnMetadata; - import java.util.Set; /** * A simple implementation of the ISQLServerBulkRecord interface that can be used to read in the basic Java data types from a delimited file where * each line represents a row of data. */ -public class SQLServerBulkCSVFileRecord extends SQLServerBulkCommon implements ISQLServerBulkRecord, java.lang.AutoCloseable { +public class SQLServerBulkCSVFileRecord extends SQLServerBulkCommon implements java.lang.AutoCloseable { /* * Resources associated with reading in the file */ @@ -50,16 +48,16 @@ public class SQLServerBulkCSVFileRecord extends SQLServerBulkCommon implements I * Delimiter to parse lines with. */ private final String delimiter; - - /* - * Class name for logging. - */ - private static final String loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkCSVFileRecord"; - - /* - * Logger - */ - private static final java.util.logging.Logger loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); + + /* + * Class name for logging. + */ + private static final String loggerClassName = "com.microsoft.sqlserver.jdbc.SQLServerBulkCSVFileRecord"; + + /* + * Logger + */ + private static final java.util.logging.Logger loggerExternal = java.util.logging.Logger.getLogger(loggerClassName); /** * Creates a simple reader to parse data from a delimited file with the given encoding. @@ -120,7 +118,7 @@ else if (null == delimiter) { loggerExternal.exiting(loggerClassName, "SQLServerBulkCSVFileRecord"); } - + /** * Creates a simple reader to parse data from a delimited file with the given encoding. * @@ -243,6 +241,7 @@ public void close() throws SQLServerException { loggerExternal.exiting(loggerClassName, "close"); } + @Override public DateTimeFormatter getColumnDateTimeFormatter(int column) { return columnMetadata.get(column).dateTimeFormatter; } @@ -283,11 +282,13 @@ public Object[] getRowData() throws SQLServerException { return null; else { // Binary data may be corrupted - // The limit in split() function should be a negative value, otherwise trailing empty strings are discarded. + // The limit in split() function should be a negative value, + // otherwise trailing empty strings are discarded. // Empty string is returned if there is no value. String[] data = currentLine.split(delimiter, -1); - // Cannot go directly from String[] to Object[] and expect it to act as an array. + // Cannot go directly from String[] to Object[] and expect it to act + // as an array. Object[] dataRow = new Object[data.length]; for (Entry pair : columnMetadata.entrySet()) { @@ -320,7 +321,8 @@ public Object[] getRowData() throws SQLServerException { * inserted into an numeric column. Our implementation does the same. */ case Types.INTEGER: { - // Formatter to remove the decimal part as SQL Server floors the decimal in integer types + // Formatter to remove the decimal part as SQL + // Server floors the decimal in integer types DecimalFormat decimalFormatter = new DecimalFormat("#"); decimalFormatter.setRoundingMode(RoundingMode.DOWN); String formatedfInput = decimalFormatter.format(Double.parseDouble(data[pair.getKey() - 1])); @@ -330,7 +332,8 @@ public Object[] getRowData() throws SQLServerException { case Types.TINYINT: case Types.SMALLINT: { - // Formatter to remove the decimal part as SQL Server floors the decimal in integer types + // Formatter to remove the decimal part as SQL + // Server floors the decimal in integer types DecimalFormat decimalFormatter = new DecimalFormat("#"); decimalFormatter.setRoundingMode(RoundingMode.DOWN); String formatedfInput = decimalFormatter.format(Double.parseDouble(data[pair.getKey() - 1])); @@ -342,10 +345,11 @@ public Object[] getRowData() throws SQLServerException { BigDecimal bd = new BigDecimal(data[pair.getKey() - 1].trim()); try { dataRow[pair.getKey() - 1] = bd.setScale(0, RoundingMode.DOWN).longValueExact(); - } catch (ArithmeticException ex) { + } + 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, ex); + throw new SQLServerException(form.format(new Object[] {value, JDBCType.of(cm.columnType)}), null, 0, ex); } break; } @@ -359,10 +363,12 @@ public Object[] getRowData() throws SQLServerException { case Types.BIT: { // "true" => 1, "false" => 0 - // Any non-zero value (integer/double) => 1, 0/0.0 => 0 + // Any non-zero value (integer/double) => 1, 0/0.0 + // => 0 try { dataRow[pair.getKey() - 1] = (0 == Double.parseDouble(data[pair.getKey() - 1])) ? Boolean.FALSE : Boolean.TRUE; - } catch (NumberFormatException e) { + } + catch (NumberFormatException e) { dataRow[pair.getKey() - 1] = Boolean.parseBoolean(data[pair.getKey() - 1]); } break; @@ -387,20 +393,20 @@ public Object[] getRowData() throws SQLServerException { * 'BULK INSERT' except that we will allow 0x prefix whereas 'BULK INSERT' command does not allow 0x prefix. A BULK INSERT * example: A sample csv file containing data for 2 binary columns and 1 row: 61,62 Table definition: create table t1(c1 * varbinary(10), c2 varbinary(10)) BULK INSERT command: bulk insert t1 from 'C:\in.csv' - * with(DATAFILETYPE='char',firstrow=1,FIELDTERMINATOR=',') select * from t1 shows 1 row with columns: 0x61, 0x62 + * with(DATAFILETYPE='char',firstrow=1, FIELDTERMINATOR=',') select * from t1 shows 1 row with columns: 0x61, 0x62 */ // Strip off 0x if present. String binData = data[pair.getKey() - 1].trim(); if (binData.startsWith("0x") || binData.startsWith("0X")) { dataRow[pair.getKey() - 1] = binData.substring(2); - } else { + } + else { dataRow[pair.getKey() - 1] = binData; } break; } - case java.sql.Types.TIME_WITH_TIMEZONE: - { + case java.sql.Types.TIME_WITH_TIMEZONE: { OffsetTime offsetTimeValue; // The per-column DateTimeFormatter gets priority. @@ -415,8 +421,7 @@ else if (timeFormatter != null) break; } - case java.sql.Types.TIMESTAMP_WITH_TIMEZONE: - { + case java.sql.Types.TIMESTAMP_WITH_TIMEZONE: { OffsetDateTime offsetDateTimeValue; // The per-column DateTimeFormatter gets priority. @@ -456,17 +461,19 @@ else if (dateTimeFormatter != null) * * Handling delimiters in data: Excel allows comma in data when data is surrounded with quotes. For example, * "Hello, world" is treated as one cell. BCP and BULK INSERT deos not allow field terminators in data: - * https://technet.microsoft.com/en-us/library/aa196735%28v=sql.80%29.aspx?f=255&MSPPError=-2147217396 + * https://technet.microsoft.com/en-us/library/ aa196735%28v=sql.80%29.aspx?f=255&MSPPError=- 2147217396 */ dataRow[pair.getKey() - 1] = data[pair.getKey() - 1]; break; } } - } catch (IllegalArgumentException e) { + } + 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, e); - } catch (ArrayIndexOutOfBoundsException e) { + throw new SQLServerException(form.format(new Object[] {value, JDBCType.of(cm.columnType)}), null, 0, e); + } + catch (ArrayIndexOutOfBoundsException e) { throw new SQLServerException(SQLServerException.getErrString("R_DataSchemaMismatch"), e); } @@ -474,7 +481,7 @@ else if (dateTimeFormatter != null) return dataRow; } } - + @Override void addColumnMetadataInternal(int positionInSource, String name, diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java index 383b4c793..dee5350cc 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCommon.java @@ -13,193 +13,147 @@ import java.util.Map; import java.util.Map.Entry; -abstract class SQLServerBulkCommon { - - /* - * Class to represent the column metadata - */ - protected class ColumnMetadata { - String columnName; - int columnType; - int precision; - int scale; - DateTimeFormatter dateTimeFormatter = null; - - ColumnMetadata(String name, - int type, - int precision, - int scale, - DateTimeFormatter dateTimeFormatter) { - columnName = name; - columnType = type; - this.precision = precision; - this.scale = scale; - this.dateTimeFormatter = dateTimeFormatter; - } - } - - /* - * Contains all the column names if firstLineIsColumnNames is true - */ - protected String[] columnNames = null; - - /* - * Metadata to represent the columns in the batch/file. Each column should be mapped to its corresponding position within the parameter (from - * position 1 and onwards) - */ - protected Map columnMetadata; - - /* - * Contains the format that java.sql.Types.TIMESTAMP_WITH_TIMEZONE data should be read in as. - */ - protected DateTimeFormatter dateTimeFormatter = null; - - /* - * Contains the format that java.sql.Types.TIME_WITH_TIMEZONE data should be read in as. - */ - protected DateTimeFormatter timeFormatter = null; - - /** - * Adds metadata for the given column in the batch/file. - * - * @param positionInSource - * Indicates which column the metadata is for. Columns start at 1. - * @param name - * Name for the column (optional if only using column ordinal in a mapping for SQLServerBulkCopy operation) - * @param jdbcType - * JDBC data type of the column - * @param precision - * Precision for the column (ignored for the appropriate data types) - * @param scale - * Scale for the column (ignored for the appropriate data types) - * @param dateTimeFormatter - * format to parse data that is sent - * @throws SQLServerException - * when an error occurs - */ - public void addColumnMetadata(int positionInSource, - String name, - int jdbcType, - int precision, - int scale, - DateTimeFormatter dateTimeFormatter) throws SQLServerException { - addColumnMetadataInternal(positionInSource, name, jdbcType, precision, scale, dateTimeFormatter); - } - - /** - * Adds metadata for the given column in the batch/file. - * - * @param positionInSource - * Indicates which column the metadata is for. Columns start at 1. - * @param name - * Name for the column (optional if only using column ordinal in a mapping for SQLServerBulkCopy operation) - * @param jdbcType - * JDBC data type of the column - * @param precision - * Precision for the column (ignored for the appropriate data types) - * @param scale - * Scale for the column (ignored for the appropriate data types) - * @throws SQLServerException - * when an error occurs - */ - public void addColumnMetadata(int positionInSource, - String name, - int jdbcType, - int precision, - int scale) throws SQLServerException { - addColumnMetadataInternal(positionInSource, name, jdbcType, precision, scale, null); - } - - /** - * Adds metadata for the given column in the batch/file. - * - * @param positionInSource - * Indicates which column the metadata is for. Columns start at 1. - * @param name - * Name for the column (optional if only using column ordinal in a mapping for SQLServerBulkCopy operation) - * @param jdbcType - * JDBC data type of the column - * @param precision - * Precision for the column (ignored for the appropriate data types) - * @param scale - * Scale for the column (ignored for the appropriate data types) - * @param dateTimeFormatter - * format to parse data that is sent - * @throws SQLServerException - * when an error occurs - */ - void addColumnMetadataInternal(int positionInSource, - String name, - int jdbcType, - int precision, - int scale, - DateTimeFormatter dateTimeFormatter) throws SQLServerException { - } - - /** - * Set the format for reading in dates from the batch/file. - * - * @param dateTimeFormat - * format to parse data sent as java.sql.Types.TIMESTAMP_WITH_TIMEZONE - */ - public void setTimestampWithTimezoneFormat(String dateTimeFormat) { - this.dateTimeFormatter = DateTimeFormatter.ofPattern(dateTimeFormat); - } - - /** - * Set the format for reading in dates from the batch/file. - * - * @param dateTimeFormatter - * format to parse data sent as java.sql.Types.TIMESTAMP_WITH_TIMEZONE - */ - public void setTimestampWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { - this.dateTimeFormatter = dateTimeFormatter; - } - - /** - * Set the format for reading in dates from the batch/file. - * - * @param timeFormat - * format to parse data sent as java.sql.Types.TIME_WITH_TIMEZONE - */ - public void setTimeWithTimezoneFormat(String timeFormat) { - this.timeFormatter = DateTimeFormatter.ofPattern(timeFormat); - } - - /** - * Set the format for reading in dates from the batch/file. - * - * @param dateTimeFormatter - * format to parse data sent as java.sql.Types.TIME_WITH_TIMEZONE - */ - public void setTimeWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { - this.timeFormatter = dateTimeFormatter; - } - - /* - * Helper method to throw a SQLServerExeption with the invalidArgument message and given argument. - */ - protected void throwInvalidArgument(String argument) throws SQLServerException { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument")); - Object[] msgArgs = {argument}; - SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, false); - } - - /* - * Method to throw a SQLServerExeption for duplicate column names - */ - protected void checkDuplicateColumnName(int positionInTable, - String colName) throws SQLServerException { - - if (null != colName && colName.trim().length() != 0) { - for (Entry entry : columnMetadata.entrySet()) { - // duplicate check is not performed in case of same positionInTable value - if (null != entry && entry.getKey() != positionInTable) { - if (null != entry.getValue() && colName.trim().equalsIgnoreCase(entry.getValue().columnName)) { - throw new SQLServerException(SQLServerException.getErrString("R_BulkDataDuplicateColumn"), null); - } - } - } - } - } +abstract class SQLServerBulkCommon implements ISQLServerBulkRecord { + + /* + * Class to represent the column metadata + */ + protected class ColumnMetadata { + String columnName; + int columnType; + int precision; + int scale; + DateTimeFormatter dateTimeFormatter = null; + + ColumnMetadata(String name, int type, int precision, int scale, + DateTimeFormatter dateTimeFormatter) { + columnName = name; + columnType = type; + this.precision = precision; + this.scale = scale; + this.dateTimeFormatter = dateTimeFormatter; + } + } + + /* + * Contains all the column names if firstLineIsColumnNames is true + */ + protected String[] columnNames = null; + + /* + * Metadata to represent the columns in the batch/file. Each column should + * be mapped to its corresponding position within the parameter (from + * position 1 and onwards) + */ + protected Map columnMetadata; + + /* + * Contains the format that java.sql.Types.TIMESTAMP_WITH_TIMEZONE data + * should be read in as. + */ + protected DateTimeFormatter dateTimeFormatter = null; + + /* + * Contains the format that java.sql.Types.TIME_WITH_TIMEZONE data should be + * read in as. + */ + protected DateTimeFormatter timeFormatter = null; + + @Override + public void addColumnMetadata(int positionInSource, String name, + int jdbcType, int precision, int scale, + DateTimeFormatter dateTimeFormatter) throws SQLServerException { + addColumnMetadataInternal(positionInSource, name, jdbcType, precision, + scale, dateTimeFormatter); + } + + @Override + public void addColumnMetadata(int positionInSource, String name, + int jdbcType, int precision, int scale) throws SQLServerException { + addColumnMetadataInternal(positionInSource, name, jdbcType, precision, + scale, null); + } + + /** + * Adds metadata for the given column in the batch/file. + * + * @param positionInSource + * Indicates which column the metadata is for. Columns start at + * 1. + * @param name + * Name for the column (optional if only using column ordinal in + * a mapping for SQLServerBulkCopy operation) + * @param jdbcType + * JDBC data type of the column + * @param precision + * Precision for the column (ignored for the appropriate data + * types) + * @param scale + * Scale for the column (ignored for the appropriate data types) + * @param dateTimeFormatter + * format to parse data that is sent + * @throws SQLServerException + * when an error occurs + */ + void addColumnMetadataInternal(int positionInSource, String name, + int jdbcType, int precision, int scale, + DateTimeFormatter dateTimeFormatter) throws SQLServerException { + } + + @Override + public void setTimestampWithTimezoneFormat(String dateTimeFormat) { + this.dateTimeFormatter = DateTimeFormatter.ofPattern(dateTimeFormat); + } + + @Override + public void setTimestampWithTimezoneFormat( + DateTimeFormatter dateTimeFormatter) { + this.dateTimeFormatter = dateTimeFormatter; + } + + @Override + public void setTimeWithTimezoneFormat(String timeFormat) { + this.timeFormatter = DateTimeFormatter.ofPattern(timeFormat); + } + + @Override + public void setTimeWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { + this.timeFormatter = dateTimeFormatter; + } + + /* + * Helper method to throw a SQLServerExeption with the invalidArgument + * message and given argument. + */ + protected void throwInvalidArgument(String argument) + throws SQLServerException { + MessageFormat form = new MessageFormat( + SQLServerException.getErrString("R_invalidArgument")); + Object[] msgArgs = {argument}; + SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), + null, false); + } + + /* + * Method to throw a SQLServerExeption for duplicate column names + */ + protected void checkDuplicateColumnName(int positionInTable, String colName) + throws SQLServerException { + + if (null != colName && colName.trim().length() != 0) { + for (Entry entry : columnMetadata + .entrySet()) { + // duplicate check is not performed in case of same + // positionInTable value + if (null != entry && entry.getKey() != positionInTable) { + if (null != entry.getValue() && colName.trim() + .equalsIgnoreCase(entry.getValue().columnName)) { + throw new SQLServerException(SQLServerException + .getErrString("R_BulkDataDuplicateColumn"), + null); + } + } + } + } + } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java index 647d77f64..9122c44fa 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java @@ -26,9 +26,12 @@ import java.sql.SQLException; import java.sql.Timestamp; import java.text.MessageFormat; +import java.time.DateTimeException; import java.time.OffsetDateTime; import java.time.OffsetTime; import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; @@ -46,6 +49,8 @@ import javax.sql.RowSet; +import microsoft.sql.DateTimeOffset; + /** * Lets you efficiently bulk load a SQL Server table with data from another source.
*
@@ -55,8 +60,13 @@ * The SQLServerBulkCopy class can be used to write data only to SQL Server tables. However, the data source is not limited to SQL Server; any data * source can be used, as long as the data can be read with a ResultSet or ISQLServerBulkRecord instance. */ -public class SQLServerBulkCopy implements java.lang.AutoCloseable { - /* +public class SQLServerBulkCopy implements java.lang.AutoCloseable, java.io.Serializable { + /** + * + */ + private static final long serialVersionUID = 1989903904654306244L; + + /* * Class to represent the column mappings between the source and destination table */ private class ColumnMapping { @@ -3079,10 +3089,63 @@ protected Object getTemporalObjectFromCSVWithFormatter(String valueStrUntrimmed, int srcJdbcType, int srcColOrdinal, DateTimeFormatter dateTimeFormatter) throws SQLServerException { - SQLServerBulkCopy42Helper.getTemporalObjectFromCSVWithFormatter(valueStrUntrimmed, srcJdbcType, srcColOrdinal, dateTimeFormatter, connection, - this); - - return null; + try { + TemporalAccessor ta = dateTimeFormatter.parse(valueStrUntrimmed); + + int taHour, taMin, taSec, taYear, taMonth, taDay, taNano, taOffsetSec; + taHour = taMin = taSec = taYear = taMonth = taDay = taNano = taOffsetSec = 0; + if (ta.isSupported(ChronoField.NANO_OF_SECOND)) + taNano = ta.get(ChronoField.NANO_OF_SECOND); + if (ta.isSupported(ChronoField.OFFSET_SECONDS)) + taOffsetSec = ta.get(ChronoField.OFFSET_SECONDS); + if (ta.isSupported(ChronoField.HOUR_OF_DAY)) + taHour = ta.get(ChronoField.HOUR_OF_DAY); + if (ta.isSupported(ChronoField.MINUTE_OF_HOUR)) + taMin = ta.get(ChronoField.MINUTE_OF_HOUR); + if (ta.isSupported(ChronoField.SECOND_OF_MINUTE)) + taSec = ta.get(ChronoField.SECOND_OF_MINUTE); + if (ta.isSupported(ChronoField.DAY_OF_MONTH)) + taDay = ta.get(ChronoField.DAY_OF_MONTH); + if (ta.isSupported(ChronoField.MONTH_OF_YEAR)) + taMonth = ta.get(ChronoField.MONTH_OF_YEAR); + if (ta.isSupported(ChronoField.YEAR)) + taYear = ta.get(ChronoField.YEAR); + + Calendar cal = new GregorianCalendar(new SimpleTimeZone(taOffsetSec * 1000, "")); + cal.clear(); + cal.set(Calendar.HOUR_OF_DAY, taHour); + cal.set(Calendar.MINUTE, taMin); + cal.set(Calendar.SECOND, taSec); + cal.set(Calendar.DATE, taDay); + cal.set(Calendar.MONTH, taMonth - 1); + cal.set(Calendar.YEAR, taYear); + int fractionalSecondsLength = Integer.toString(taNano).length(); + for (int i = 0; i < (9 - fractionalSecondsLength); i++) + taNano *= 10; + Timestamp ts = new Timestamp(cal.getTimeInMillis()); + ts.setNanos(taNano); + + switch (srcJdbcType) { + case java.sql.Types.TIMESTAMP: + return ts; + case java.sql.Types.TIME: + // Time is returned as Timestamp to preserve nano seconds. + cal.set(connection.baseYear(), Calendar.JANUARY, 01); + ts = new java.sql.Timestamp(cal.getTimeInMillis()); + ts.setNanos(taNano); + return new java.sql.Timestamp(ts.getTime()); + case java.sql.Types.DATE: + return new java.sql.Date(ts.getTime()); + case microsoft.sql.Types.DATETIMEOFFSET: + return DateTimeOffset.valueOf(ts, taOffsetSec / 60); + } + } + catch (DateTimeException | ArithmeticException e) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ParsingError")); + Object[] msgArgs = {JDBCType.of(srcJdbcType)}; + throw new SQLServerException(this, form.format(msgArgs), null, 0, false); + } + return valueStrUntrimmed; } private Object getTemporalObjectFromCSV(Object value, diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy42Helper.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy42Helper.java deleted file mode 100644 index ea9ff510f..000000000 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy42Helper.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.jdbc; - -import java.sql.Timestamp; -import java.text.MessageFormat; -import java.time.DateTimeException; -import java.time.format.DateTimeFormatter; -import java.time.temporal.ChronoField; -import java.time.temporal.TemporalAccessor; -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.SimpleTimeZone; - -import microsoft.sql.DateTimeOffset; - -/** - * - * This class is separated from SQLServerBulkCopy class to resolve run-time error of missing Java 8 types when running with Java 7 - * - */ -class SQLServerBulkCopy42Helper { - static Object getTemporalObjectFromCSVWithFormatter(String valueStrUntrimmed, - int srcJdbcType, - int srcColOrdinal, - DateTimeFormatter dateTimeFormatter, - SQLServerConnection connection, - SQLServerBulkCopy sqlServerBC) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - try { - TemporalAccessor ta = dateTimeFormatter.parse(valueStrUntrimmed); - - int taHour, taMin, taSec, taYear, taMonth, taDay, taNano, taOffsetSec; - taHour = taMin = taSec = taYear = taMonth = taDay = taNano = taOffsetSec = 0; - if (ta.isSupported(ChronoField.NANO_OF_SECOND)) - taNano = ta.get(ChronoField.NANO_OF_SECOND); - if (ta.isSupported(ChronoField.OFFSET_SECONDS)) - taOffsetSec = ta.get(ChronoField.OFFSET_SECONDS); - if (ta.isSupported(ChronoField.HOUR_OF_DAY)) - taHour = ta.get(ChronoField.HOUR_OF_DAY); - if (ta.isSupported(ChronoField.MINUTE_OF_HOUR)) - taMin = ta.get(ChronoField.MINUTE_OF_HOUR); - if (ta.isSupported(ChronoField.SECOND_OF_MINUTE)) - taSec = ta.get(ChronoField.SECOND_OF_MINUTE); - if (ta.isSupported(ChronoField.DAY_OF_MONTH)) - taDay = ta.get(ChronoField.DAY_OF_MONTH); - if (ta.isSupported(ChronoField.MONTH_OF_YEAR)) - taMonth = ta.get(ChronoField.MONTH_OF_YEAR); - if (ta.isSupported(ChronoField.YEAR)) - taYear = ta.get(ChronoField.YEAR); - - Calendar cal = new GregorianCalendar(new SimpleTimeZone(taOffsetSec * 1000, "")); - cal.clear(); - cal.set(Calendar.HOUR_OF_DAY, taHour); - cal.set(Calendar.MINUTE, taMin); - cal.set(Calendar.SECOND, taSec); - cal.set(Calendar.DATE, taDay); - cal.set(Calendar.MONTH, taMonth - 1); - cal.set(Calendar.YEAR, taYear); - int fractionalSecondsLength = Integer.toString(taNano).length(); - for (int i = 0; i < (9 - fractionalSecondsLength); i++) - taNano *= 10; - Timestamp ts = new Timestamp(cal.getTimeInMillis()); - ts.setNanos(taNano); - - switch (srcJdbcType) { - case java.sql.Types.TIMESTAMP: - return ts; - case java.sql.Types.TIME: - // Time is returned as Timestamp to preserve nano seconds. - cal.set(connection.baseYear(), Calendar.JANUARY, 01); - ts = new java.sql.Timestamp(cal.getTimeInMillis()); - ts.setNanos(taNano); - return new java.sql.Timestamp(ts.getTime()); - case java.sql.Types.DATE: - return new java.sql.Date(ts.getTime()); - case microsoft.sql.Types.DATETIMEOFFSET: - return DateTimeOffset.valueOf(ts, taOffsetSec / 60); - } - } - catch (DateTimeException | ArithmeticException e) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ParsingError")); - Object[] msgArgs = {JDBCType.of(srcJdbcType)}; - throw new SQLServerException(sqlServerBC, form.format(msgArgs), null, 0, false); - } - // unreachable code. Need to do to compile from Eclipse. - return valueStrUntrimmed; - } -} \ No newline at end of file diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java index c30eab2fa..57da43fff 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java @@ -22,11 +22,10 @@ import java.sql.ResultSet; import java.sql.RowId; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; +import java.sql.SQLType; import java.sql.SQLXML; import java.sql.Time; import java.sql.Timestamp; -import java.sql.Types; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Calendar; @@ -43,7 +42,7 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement implements ISQLServerCallableStatement { /** the call param names */ - private ArrayList paramNames; + private ArrayList parameterNames; /** Number of registered OUT parameters */ int nOutParams = 0; @@ -87,6 +86,7 @@ String getClassNameInternal() { super(connection, sql, nRSType, nRSConcur, stmtColEncSetting); } + @Override public void registerOutParameter(int index, int sqlType) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -343,7 +343,8 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException { } } - /* L0 */ public void registerOutParameter(int index, + @Override + public void registerOutParameter(int index, int sqlType, String typeName) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -356,12 +357,12 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } - /* L0 */ public void registerOutParameter(int index, + @Override + public void registerOutParameter(int index, int sqlType, int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", - new Object[] {index, sqlType, scale}); + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {index, sqlType, scale}); checkClosed(); @@ -371,13 +372,13 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } + @Override public void registerOutParameter(int index, int sqlType, int precision, int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", - new Object[] {index, sqlType, scale, precision}); + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {index, sqlType, scale, precision}); checkClosed(); @@ -453,6 +454,7 @@ private Object getSQLXMLInternal(int parameterIndex) throws SQLServerException { return value; } + @Override public int getInt(int index) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getInt", index); checkClosed(); @@ -461,14 +463,16 @@ public int getInt(int index) throws SQLServerException { return null != value ? value : 0; } - public int getInt(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getInt", sCol); + @Override + public int getInt(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getInt", parameterName); checkClosed(); - Integer value = (Integer) getValue(findColumn(sCol), JDBCType.INTEGER); + Integer value = (Integer) getValue(findColumn(parameterName), JDBCType.INTEGER); loggerExternal.exiting(getClassNameLogging(), "getInt", value); return null != value ? value : 0; } + @Override public String getString(int index) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getString", index); checkClosed(); @@ -480,12 +484,13 @@ public String getString(int index) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "getString", value); return value; } - - public String getString(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getString", sCol); + + @Override + public String getString(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getString", parameterName); checkClosed(); String value = null; - Object objectValue = getValue(findColumn(sCol), JDBCType.CHAR); + Object objectValue = getValue(findColumn(parameterName), JDBCType.CHAR); if (null != objectValue) { value = objectValue.toString(); } @@ -493,6 +498,7 @@ public String getString(String sCol) throws SQLServerException { return value; } + @Override public final String getNString(int parameterIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNString", parameterIndex); checkClosed(); @@ -501,6 +507,7 @@ public final String getNString(int parameterIndex) throws SQLException { return value; } + @Override public final String getNString(String parameterName) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNString", parameterName); checkClosed(); @@ -510,6 +517,7 @@ public final String getNString(String parameterName) throws SQLException { } @Deprecated + @Override public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -523,8 +531,9 @@ public BigDecimal getBigDecimal(int parameterIndex, } @Deprecated + @Override public BigDecimal getBigDecimal(String parameterName, - int scale) throws SQLException { + int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getBigDecimal", new Object[] {parameterName, scale}); checkClosed(); @@ -535,6 +544,7 @@ public BigDecimal getBigDecimal(String parameterName, return value; } + @Override public boolean getBoolean(int index) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getBoolean", index); checkClosed(); @@ -543,14 +553,16 @@ public boolean getBoolean(int index) throws SQLServerException { return null != value ? value : false; } - public boolean getBoolean(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getBoolean", sCol); + @Override + public boolean getBoolean(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getBoolean", parameterName); checkClosed(); - Boolean value = (Boolean) getValue(findColumn(sCol), JDBCType.BIT); + Boolean value = (Boolean) getValue(findColumn(parameterName), JDBCType.BIT); loggerExternal.exiting(getClassNameLogging(), "getBoolean", value); return null != value ? value : false; } + @Override public byte getByte(int index) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getByte", index); checkClosed(); @@ -560,15 +572,17 @@ public byte getByte(int index) throws SQLServerException { return byteValue; } - public byte getByte(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getByte", sCol); + @Override + public byte getByte(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getByte", parameterName); checkClosed(); - Short shortValue = (Short) getValue(findColumn(sCol), JDBCType.TINYINT); + Short shortValue = (Short) getValue(findColumn(parameterName), JDBCType.TINYINT); byte byteValue = (null != shortValue) ? shortValue.byteValue() : 0; loggerExternal.exiting(getClassNameLogging(), "getByte", byteValue); return byteValue; } + @Override public byte[] getBytes(int index) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getBytes", index); checkClosed(); @@ -577,14 +591,16 @@ public byte[] getBytes(int index) throws SQLServerException { return value; } - public byte[] getBytes(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getBytes", sCol); + @Override + public byte[] getBytes(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getBytes", parameterName); checkClosed(); - byte[] value = (byte[]) getValue(findColumn(sCol), JDBCType.BINARY); + byte[] value = (byte[]) getValue(findColumn(parameterName), JDBCType.BINARY); loggerExternal.exiting(getClassNameLogging(), "getBytes", value); return value; } + @Override public Date getDate(int index) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getDate", index); checkClosed(); @@ -593,14 +609,16 @@ public Date getDate(int index) throws SQLServerException { return value; } - public Date getDate(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getDate", sCol); + @Override + public Date getDate(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getDate", parameterName); checkClosed(); - java.sql.Date value = (java.sql.Date) getValue(findColumn(sCol), JDBCType.DATE); + java.sql.Date value = (java.sql.Date) getValue(findColumn(parameterName), JDBCType.DATE); loggerExternal.exiting(getClassNameLogging(), "getDate", value); return value; } + @Override public Date getDate(int index, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -611,16 +629,18 @@ public Date getDate(int index, return value; } - public Date getDate(String sCol, + @Override + public Date getDate(String parameterName, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "getDate", new Object[] {sCol, cal}); + loggerExternal.entering(getClassNameLogging(), "getDate", new Object[] {parameterName, cal}); checkClosed(); - java.sql.Date value = (java.sql.Date) getValue(findColumn(sCol), JDBCType.DATE, cal); + java.sql.Date value = (java.sql.Date) getValue(findColumn(parameterName), JDBCType.DATE, cal); loggerExternal.exiting(getClassNameLogging(), "getDate", value); return value; } + @Override public double getDouble(int index) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getDouble", index); checkClosed(); @@ -629,14 +649,16 @@ public double getDouble(int index) throws SQLServerException { return null != value ? value : 0; } - public double getDouble(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getDouble", sCol); + @Override + public double getDouble(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getDouble", parameterName); checkClosed(); - Double value = (Double) getValue(findColumn(sCol), JDBCType.DOUBLE); + Double value = (Double) getValue(findColumn(parameterName), JDBCType.DOUBLE); loggerExternal.exiting(getClassNameLogging(), "getDouble", value); return null != value ? value : 0; } + @Override public float getFloat(int index) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getFloat", index); checkClosed(); @@ -645,15 +667,17 @@ public float getFloat(int index) throws SQLServerException { return null != value ? value : 0; } - public float getFloat(String sCol) throws SQLServerException { + @Override + public float getFloat(String parameterName) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getFloat", sCol); + loggerExternal.entering(getClassNameLogging(), "getFloat", parameterName); checkClosed(); - Float value = (Float) getValue(findColumn(sCol), JDBCType.REAL); + Float value = (Float) getValue(findColumn(parameterName), JDBCType.REAL); loggerExternal.exiting(getClassNameLogging(), "getFloat", value); return null != value ? value : 0; } + @Override public long getLong(int index) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getLong", index); @@ -663,14 +687,16 @@ public long getLong(int index) throws SQLServerException { return null != value ? value : 0; } - public long getLong(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getLong", sCol); + @Override + public long getLong(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getLong", parameterName); checkClosed(); - Long value = (Long) getValue(findColumn(sCol), JDBCType.BIGINT); + Long value = (Long) getValue(findColumn(parameterName), JDBCType.BIGINT); loggerExternal.exiting(getClassNameLogging(), "getLong", value); return null != value ? value : 0; } + @Override public Object getObject(int index) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getObject", index); @@ -681,6 +707,7 @@ public Object getObject(int index) throws SQLServerException { return value; } + @Override public T getObject(int index, Class type) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getObject", index); @@ -763,26 +790,30 @@ else if (type == Double.class) { return type.cast(returnValue); } - public Object getObject(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getObject", sCol); + @Override + public Object getObject(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getObject", parameterName); checkClosed(); - int parameterIndex = findColumn(sCol); - Object value = getValue(parameterIndex, getterGetParam(parameterIndex).getJdbcTypeSetByUser() != null - ? getterGetParam(parameterIndex).getJdbcTypeSetByUser() : getterGetParam(parameterIndex).getJdbcType()); + int parameterIndex = findColumn(parameterName); + Object value = getValue(parameterIndex, + getterGetParam(parameterIndex).getJdbcTypeSetByUser() != null ? getterGetParam(parameterIndex).getJdbcTypeSetByUser() + : getterGetParam(parameterIndex).getJdbcType()); loggerExternal.exiting(getClassNameLogging(), "getObject", value); return value; } - public T getObject(String sCol, + @Override + public T getObject(String parameterName, Class type) throws SQLException { - loggerExternal.entering(getClassNameLogging(), "getObject", sCol); + loggerExternal.entering(getClassNameLogging(), "getObject", parameterName); checkClosed(); - int parameterIndex = findColumn(sCol); + int parameterIndex = findColumn(parameterName); T value = getObject(parameterIndex, type); loggerExternal.exiting(getClassNameLogging(), "getObject", value); return value; } + @Override public short getShort(int index) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getShort", index); @@ -792,14 +823,16 @@ public short getShort(int index) throws SQLServerException { return null != value ? value : 0; } - public short getShort(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getShort", sCol); + @Override + public short getShort(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getShort", parameterName); checkClosed(); - Short value = (Short) getValue(findColumn(sCol), JDBCType.SMALLINT); + Short value = (Short) getValue(findColumn(parameterName), JDBCType.SMALLINT); loggerExternal.exiting(getClassNameLogging(), "getShort", value); return null != value ? value : 0; } + @Override public Time getTime(int index) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getTime", index); @@ -809,14 +842,16 @@ public Time getTime(int index) throws SQLServerException { return value; } - public Time getTime(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getTime", sCol); + @Override + public Time getTime(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getTime", parameterName); checkClosed(); - java.sql.Time value = (java.sql.Time) getValue(findColumn(sCol), JDBCType.TIME); + java.sql.Time value = (java.sql.Time) getValue(findColumn(parameterName), JDBCType.TIME); loggerExternal.exiting(getClassNameLogging(), "getTime", value); return value; } + @Override public Time getTime(int index, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -827,16 +862,18 @@ public Time getTime(int index, return value; } - public Time getTime(String sCol, + @Override + public Time getTime(String parameterName, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "getTime", new Object[] {sCol, cal}); + loggerExternal.entering(getClassNameLogging(), "getTime", new Object[] {parameterName, cal}); checkClosed(); - java.sql.Time value = (java.sql.Time) getValue(findColumn(sCol), JDBCType.TIME, cal); + java.sql.Time value = (java.sql.Time) getValue(findColumn(parameterName), JDBCType.TIME, cal); loggerExternal.exiting(getClassNameLogging(), "getTime", value); return value; } + @Override public Timestamp getTimestamp(int index) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getTimestamp", index); @@ -846,14 +883,16 @@ public Timestamp getTimestamp(int index) throws SQLServerException { return value; } - public Timestamp getTimestamp(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getTimestamp", sCol); + @Override + public Timestamp getTimestamp(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getTimestamp", parameterName); checkClosed(); - java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(sCol), JDBCType.TIMESTAMP); + java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(parameterName), JDBCType.TIMESTAMP); loggerExternal.exiting(getClassNameLogging(), "getTimestamp", value); return value; } + @Override public Timestamp getTimestamp(int index, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -864,6 +903,7 @@ public Timestamp getTimestamp(int index, return value; } + @Override public Timestamp getTimestamp(String name, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -874,16 +914,7 @@ public Timestamp getTimestamp(String name, return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming - * language. - * - * @param index - * the first column is 1, the second is 2, ... - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * when an error occurs - */ + @Override public Timestamp getDateTime(int index) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getDateTime", index); @@ -893,38 +924,16 @@ public Timestamp getDateTime(int index) throws SQLServerException { return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming - * language. - * - * @param sCol - * the label for the column specified with the SQL AS clause. If the SQL AS clause was not specified, then the label is the name of the - * column - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * when an error occurs - */ - public Timestamp getDateTime(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getDateTime", sCol); + @Override + public Timestamp getDateTime(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getDateTime", parameterName); checkClosed(); - java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(sCol), JDBCType.DATETIME); + java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(parameterName), JDBCType.DATETIME); loggerExternal.exiting(getClassNameLogging(), "getDateTime", value); return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming - * language. This method uses the given calendar to construct an appropriate millisecond value for the timestamp if the underlying database does - * not store timezone information. - * - * @param index - * the first column is 1, the second is 2, ... - * @param cal - * the java.util.Calendar object to use in constructing the dateTime - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * when an error occurs - */ + @Override public Timestamp getDateTime(int index, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -935,19 +944,7 @@ public Timestamp getDateTime(int index, return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming - * language. This method uses the given calendar to construct an appropriate millisecond value for the timestamp if the underlying database does - * not store timezone information. - * - * @param name - * the name of the column - * @param cal - * the java.util.Calendar object to use in constructing the dateTime - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * when an error occurs - */ + @Override public Timestamp getDateTime(String name, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -958,16 +955,7 @@ public Timestamp getDateTime(String name, return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming - * language. - * - * @param index - * the first column is 1, the second is 2, ... - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * when an error occurs - */ + @Override public Timestamp getSmallDateTime(int index) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getSmallDateTime", index); @@ -977,36 +965,16 @@ public Timestamp getSmallDateTime(int index) throws SQLServerException { return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming - * language. - * - * @param sCol - * The name of a column. - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * when an error occurs - */ - public Timestamp getSmallDateTime(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getSmallDateTime", sCol); + @Override + public Timestamp getSmallDateTime(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getSmallDateTime", parameterName); checkClosed(); - java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(sCol), JDBCType.SMALLDATETIME); + java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(parameterName), JDBCType.SMALLDATETIME); loggerExternal.exiting(getClassNameLogging(), "getSmallDateTime", value); return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming - * language. - * - * @param index - * the first column is 1, the second is 2, ... - * @param cal - * the java.util.Calendar object to use in constructing the smalldateTime - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * when an error occurs - */ + @Override public Timestamp getSmallDateTime(int index, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1017,16 +985,7 @@ public Timestamp getSmallDateTime(int index, return value; } - /** - * - * @param name - * The name of a column - * @param cal - * the java.util.Calendar object to use in constructing the smalldateTime - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * when an error occurs - */ + @Override public Timestamp getSmallDateTime(String name, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1037,7 +996,8 @@ public Timestamp getSmallDateTime(String name, return value; } - public microsoft.sql.DateTimeOffset getDateTimeOffset(int index) throws SQLException { + @Override + public microsoft.sql.DateTimeOffset getDateTimeOffset(int index) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getDateTimeOffset", index); checkClosed(); @@ -1052,8 +1012,9 @@ public microsoft.sql.DateTimeOffset getDateTimeOffset(int index) throws SQLExcep return value; } - public microsoft.sql.DateTimeOffset getDateTimeOffset(String sCol) throws SQLException { - loggerExternal.entering(getClassNameLogging(), "getDateTimeOffset", sCol); + @Override + public microsoft.sql.DateTimeOffset getDateTimeOffset(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getDateTimeOffset", parameterName); checkClosed(); // DateTimeOffset is not supported with SQL Server versions earlier than Katmai @@ -1061,12 +1022,13 @@ public microsoft.sql.DateTimeOffset getDateTimeOffset(String sCol) throws SQLExc throw new SQLServerException(SQLServerException.getErrString("R_notSupported"), SQLState.DATA_EXCEPTION_NOT_SPECIFIC, DriverError.NOT_SET, null); - microsoft.sql.DateTimeOffset value = (microsoft.sql.DateTimeOffset) getValue(findColumn(sCol), JDBCType.DATETIMEOFFSET); + microsoft.sql.DateTimeOffset value = (microsoft.sql.DateTimeOffset) getValue(findColumn(parameterName), JDBCType.DATETIMEOFFSET); loggerExternal.exiting(getClassNameLogging(), "getDateTimeOffset", value); return value; } - /* L0 */ public boolean wasNull() throws SQLServerException { + @Override + public boolean wasNull() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "wasNull"); checkClosed(); boolean bWasNull = false; @@ -1077,213 +1039,124 @@ public microsoft.sql.DateTimeOffset getDateTimeOffset(String sCol) throws SQLExc return bWasNull; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a stream of ASCII characters. The - * value can then be read in chunks from the stream. This method is particularly suitable for retrieving large LONGVARCHAR values. - * The JDBC driver will do any necessary conversion from the database format into ASCII. - * - *

- * Note: All the data in the returned stream must be read prior to getting the value of any other column. The next call to a getter method - * implicitly closes the stream. Also, a stream may return 0 when the method InputStream.available is called whether - * there is data available or not. - * - * @param paramIndex - * the first column is 1, the second is 2, ... - * @return a Java input stream that delivers the database column value as a stream of one-byte ASCII characters; if the value is SQL - * NULL, the value returned is null - * @throws SQLServerException - * if the columnIndex is not valid; if a database access error occurs or this method is called on a closed result set - */ - public final java.io.InputStream getAsciiStream(int paramIndex) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getAsciiStream", paramIndex); + @Override + public final java.io.InputStream getAsciiStream(int parameterIndex) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getAsciiStream", parameterIndex); checkClosed(); - InputStream value = (InputStream) getStream(paramIndex, StreamType.ASCII); + InputStream value = (InputStream) getStream(parameterIndex, StreamType.ASCII); loggerExternal.exiting(getClassNameLogging(), "getAsciiStream", value); return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a stream of ASCII characters. The - * value can then be read in chunks from the stream. This method is particularly suitable for retrieving large LONGVARCHAR values. - * The JDBC driver will do any necessary conversion from the database format into ASCII. - * - *

- * Note: All the data in the returned stream must be read prior to getting the value of any other column. The next call to a getter method - * implicitly closes the stream. Also, a stream may return 0 when the method available is called whether there is data - * available or not. - * - * @param paramName - * the name of the parameter - * @return a Java input stream that delivers the database column value as a stream of one-byte ASCII characters. If the value is SQL - * NULL, the value returned is null. - * @throws SQLServerException - * if the columnLabel is not valid; if a database access error occurs or this method is called on a closed result set - */ - public final java.io.InputStream getAsciiStream(String paramName) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getAsciiStream", paramName); + @Override + public final java.io.InputStream getAsciiStream(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getAsciiStream", parameterName); checkClosed(); - InputStream value = (InputStream) getStream(findColumn(paramName), StreamType.ASCII); + InputStream value = (InputStream) getStream(findColumn(parameterName), StreamType.ASCII); loggerExternal.exiting(getClassNameLogging(), "getAsciiStream", value); return value; } - public BigDecimal getBigDecimal(int index) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getBigDecimal", index); + @Override + public BigDecimal getBigDecimal(int parameterIndex) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getBigDecimal", parameterIndex); checkClosed(); - BigDecimal value = (BigDecimal) getValue(index, JDBCType.DECIMAL); + BigDecimal value = (BigDecimal) getValue(parameterIndex, JDBCType.DECIMAL); loggerExternal.exiting(getClassNameLogging(), "getBigDecimal", value); return value; } - public BigDecimal getBigDecimal(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getBigDecimal", sCol); + @Override + public BigDecimal getBigDecimal(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getBigDecimal", parameterName); checkClosed(); - BigDecimal value = (BigDecimal) getValue(findColumn(sCol), JDBCType.DECIMAL); + BigDecimal value = (BigDecimal) getValue(findColumn(parameterName), JDBCType.DECIMAL); loggerExternal.exiting(getClassNameLogging(), "getBigDecimal", value); return value; } - /** - * Retrieves the value of the column specified as a java.math.BigDecimal object. - * - * @param index - * The zero-based ordinal of a column. - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * when an error occurs - */ - public BigDecimal getMoney(int index) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getMoney", index); + @Override + public BigDecimal getMoney(int parameterIndex) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getMoney", parameterIndex); checkClosed(); - BigDecimal value = (BigDecimal) getValue(index, JDBCType.MONEY); + BigDecimal value = (BigDecimal) getValue(parameterIndex, JDBCType.MONEY); loggerExternal.exiting(getClassNameLogging(), "getMoney", value); return value; } - /** - * Retrieves the value of the column specified as a java.math.BigDecimal object. - * - * @param sCol - * The name of a column. - * @return the column value; if the value is SQL NULL, the value returned is null. - * @throws SQLServerException - * when an error occurs - */ - public BigDecimal getMoney(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getMoney", sCol); + @Override + public BigDecimal getMoney(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getMoney", parameterName); checkClosed(); - BigDecimal value = (BigDecimal) getValue(findColumn(sCol), JDBCType.MONEY); + BigDecimal value = (BigDecimal) getValue(findColumn(parameterName), JDBCType.MONEY); loggerExternal.exiting(getClassNameLogging(), "getMoney", value); return value; } - /** - * Retrieves the value of the column specified as a java.math.BigDecimal object. - * - * @param index - * The zero-based ordinal of a column. - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * when an error occurs - */ - public BigDecimal getSmallMoney(int index) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getSmallMoney", index); + @Override + public BigDecimal getSmallMoney(int parameterIndex) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getSmallMoney", parameterIndex); checkClosed(); - BigDecimal value = (BigDecimal) getValue(index, JDBCType.SMALLMONEY); + BigDecimal value = (BigDecimal) getValue(parameterIndex, JDBCType.SMALLMONEY); loggerExternal.exiting(getClassNameLogging(), "getSmallMoney", value); return value; } - /** - * Retrieves the value of the column specified as a java.math.BigDecimal object. - * - * @param sCol - * The name of a column. - * @return the column value; if the value is SQL NULL, the value returned is null. - * @throws SQLServerException - * when an error occurs - */ - public BigDecimal getSmallMoney(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getSmallMoney", sCol); + @Override + public BigDecimal getSmallMoney(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getSmallMoney", parameterName); checkClosed(); - BigDecimal value = (BigDecimal) getValue(findColumn(sCol), JDBCType.SMALLMONEY); + BigDecimal value = (BigDecimal) getValue(findColumn(parameterName), JDBCType.SMALLMONEY); loggerExternal.exiting(getClassNameLogging(), "getSmallMoney", value); return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a stream of uninterpreted bytes. The - * value can then be read in chunks from the stream. This method is particularly suitable for retrieving large LONGVARBINARY values. - * - *

- * Note: All the data in the returned stream must be read prior to getting the value of any other column. The next call to a getter method - * implicitly closes the stream. Also, a stream may return 0 when the method InputStream.available is called whether - * there is data available or not. - * - * @param paramIndex - * the first column is 1, the second is 2, ... - * @return a Java input stream that delivers the database column value as a stream of uninterpreted bytes; if the value is SQL NULL, - * the value returned is null - * @throws SQLServerException - * if the columnIndex is not valid; if a database access error occurs or this method is called on a closed result set - */ - public final java.io.InputStream getBinaryStream(int paramIndex) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getBinaryStream", paramIndex); + @Override + public final java.io.InputStream getBinaryStream(int parameterIndex) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getBinaryStream", parameterIndex); checkClosed(); - InputStream value = (InputStream) getStream(paramIndex, StreamType.BINARY); + InputStream value = (InputStream) getStream(parameterIndex, StreamType.BINARY); loggerExternal.exiting(getClassNameLogging(), "getBinaryStream", value); return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a stream of uninterpreted - * bytes. The value can then be read in chunks from the stream. This method is particularly suitable for retrieving large - * LONGVARBINARY values. - * - *

- * Note: All the data in the returned stream must be read prior to getting the value of any other column. The next call to a getter method - * implicitly closes the stream. Also, a stream may return 0 when the method available is called whether there is data - * available or not. - * - * @param paramName - * the name of the parameter - * @return a Java input stream that delivers the database column value as a stream of uninterpreted bytes; if the value is SQL NULL, - * the result is null - * @throws SQLServerException - * if the columnLabel is not valid; if a database access error occurs or this method is called on a closed result set - */ - public final java.io.InputStream getBinaryStream(String paramName) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getBinaryStream", paramName); + @Override + public final java.io.InputStream getBinaryStream(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getBinaryStream", parameterName); checkClosed(); - InputStream value = (InputStream) getStream(findColumn(paramName), StreamType.BINARY); + InputStream value = (InputStream) getStream(findColumn(parameterName), StreamType.BINARY); loggerExternal.exiting(getClassNameLogging(), "getBinaryStream", value); return value; } - public Blob getBlob(int index) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getBlob", index); + @Override + public Blob getBlob(int parameterIndex) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getBlob", parameterIndex); checkClosed(); - Blob value = (Blob) getValue(index, JDBCType.BLOB); + Blob value = (Blob) getValue(parameterIndex, JDBCType.BLOB); loggerExternal.exiting(getClassNameLogging(), "getBlob", value); return value; } - public Blob getBlob(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getBlob", sCol); + @Override + public Blob getBlob(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getBlob", parameterName); checkClosed(); - Blob value = (Blob) getValue(findColumn(sCol), JDBCType.BLOB); + Blob value = (Blob) getValue(findColumn(parameterName), JDBCType.BLOB); loggerExternal.exiting(getClassNameLogging(), "getBlob", value); return value; } - public final java.io.Reader getCharacterStream(int paramIndex) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getCharacterStream", paramIndex); + @Override + public final java.io.Reader getCharacterStream(int parameterIndex) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getCharacterStream", parameterIndex); checkClosed(); - Reader reader = (Reader) getStream(paramIndex, StreamType.CHARACTER); + Reader reader = (Reader) getStream(parameterIndex, StreamType.CHARACTER); loggerExternal.exiting(getClassNameLogging(), "getCharacterStream", reader); return reader; } + @Override public final java.io.Reader getCharacterStream(String parameterName) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getCharacterStream", parameterName); checkClosed(); @@ -1292,6 +1165,7 @@ public final java.io.Reader getCharacterStream(String parameterName) throws SQLE return reader; } + @Override public final java.io.Reader getNCharacterStream(int parameterIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNCharacterStream", parameterIndex); checkClosed(); @@ -1300,6 +1174,7 @@ public final java.io.Reader getNCharacterStream(int parameterIndex) throws SQLEx return reader; } + @Override public final java.io.Reader getNCharacterStream(String parameterName) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNCharacterStream", parameterName); checkClosed(); @@ -1322,22 +1197,25 @@ void closeActiveStream() throws SQLServerException { } } - public Clob getClob(int index) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getClob", index); + @Override + public Clob getClob(int parameterIndex) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getClob", parameterIndex); checkClosed(); - Clob clob = (Clob) getValue(index, JDBCType.CLOB); + Clob clob = (Clob) getValue(parameterIndex, JDBCType.CLOB); loggerExternal.exiting(getClassNameLogging(), "getClob", clob); return clob; } - public Clob getClob(String sCol) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getClob", sCol); + @Override + public Clob getClob(String parameterName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getClob", parameterName); checkClosed(); - Clob clob = (Clob) getValue(findColumn(sCol), JDBCType.CLOB); + Clob clob = (Clob) getValue(findColumn(parameterName), JDBCType.CLOB); loggerExternal.exiting(getClassNameLogging(), "getClob", clob); return clob; } + @Override public NClob getNClob(int parameterIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNClob", parameterIndex); checkClosed(); @@ -1346,6 +1224,7 @@ public NClob getNClob(int parameterIndex) throws SQLException { return nClob; } + @Override public NClob getNClob(String parameterName) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNClob", parameterName); checkClosed(); @@ -1354,36 +1233,42 @@ public NClob getNClob(String parameterName) throws SQLException { return nClob; } - /* L0 */ public Object getObject(int index, - java.util.Map> map) throws SQLServerException { - NotImplemented(); + @Override + public Object getObject(int parameterIndex, + java.util.Map> map) throws SQLException { + SQLServerException.throwNotSupportedException(connection, this); return null; } - /* L3 */ public Object getObject(String sCol, - java.util.Map> m) throws SQLServerException { + @Override + public Object getObject(String parameterName, + java.util.Map> m) throws SQLException { checkClosed(); - return getObject(findColumn(sCol), m); + return getObject(findColumn(parameterName), m); } - /* L0 */ public Ref getRef(int i) throws SQLServerException { - NotImplemented(); + @Override + public Ref getRef(int parameterIndex) throws SQLException { + SQLServerException.throwNotSupportedException(connection, this); return null; } - /* L3 */ public Ref getRef(String sCol) throws SQLServerException { + @Override + public Ref getRef(String parameterName) throws SQLException { checkClosed(); - return getRef(findColumn(sCol)); + return getRef(findColumn(parameterName)); } - /* L0 */ public java.sql.Array getArray(int i) throws SQLServerException { - NotImplemented(); + @Override + public java.sql.Array getArray(int parameterIndex) throws SQLException { + SQLServerException.throwNotSupportedException(connection, this); return null; } - /* L3 */ public java.sql.Array getArray(String sCol) throws SQLServerException { + @Override + public java.sql.Array getArray(String parameterName) throws SQLException { checkClosed(); - return getArray(findColumn(sCol)); + return getArray(findColumn(parameterName)); } /* JDBC 3.0 */ @@ -1397,14 +1282,13 @@ public NClob getNClob(String parameterName) throws SQLException { * when an error occurs * @return the index */ - /* L3 */ private int findColumn(String columnName) throws SQLServerException { - if (paramNames == null) { - SQLServerStatement s = null; - try { + private int findColumn(String columnName) throws SQLServerException { + if (parameterNames == null) { + try (SQLServerStatement s = (SQLServerStatement) connection.createStatement()) { // Note we are concatenating the information from the passed in sql, not any arguments provided by the user // if the user can execute the sql, any fragments of it is potentially executed via the meta data call through injection // is not a security issue. - s = (SQLServerStatement) connection.createStatement(); + ThreePartName threePartName = ThreePartName.parse(procedureName); StringBuilder metaQuery = new StringBuilder("exec sp_sproc_columns "); if (null != threePartName.getDatabasePart()) { @@ -1424,7 +1308,7 @@ public NClob getNClob(String parameterName) throws SQLException { metaQuery.append(" , @ODBCVer=3"); } else { - // This should rarely happen, this will only happen if we cant find the stored procedure name + // This should rarely happen, this will only happen if we can't find the stored procedure name // invalidly formatted call syntax. MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_parameterNotDefinedForProcedure")); Object[] msgArgs = {columnName, ""}; @@ -1432,36 +1316,34 @@ public NClob getNClob(String parameterName) throws SQLException { } ResultSet rs = s.executeQueryInternal(metaQuery.toString()); - paramNames = new ArrayList<>(); + parameterNames = new ArrayList<>(); while (rs.next()) { - String sCol = rs.getString(4); - paramNames.add(sCol.trim()); + String parameterName = rs.getString(4); + parameterNames.add(parameterName.trim()); } } catch (SQLException e) { SQLServerException.makeFromDriverError(connection, this, e.toString(), null, false); } - finally { - if (null != s) - s.close(); - } + } int l = 0; - if (paramNames != null) - l = paramNames.size(); - if (l == 0)//Server didn't return anything, user might not have access - return 1;//attempting to look up the first column will return no access exception + if (parameterNames != null) + l = parameterNames.size(); + if (l == 0)// Server didn't return anything, user might not have access + return 1;// attempting to look up the first column will return no access exception - // handle `@name` as well as `name`, since `@name` is what's returned + // 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 { + } + else { columnNameWithoutAtSign = columnName; } - + // In order to be as accurate as possible when locating parameter name // indexes, as well as be deterministic when running on various client // locales, we search for parameter names using the following scheme: @@ -1474,7 +1356,7 @@ public NClob getNClob(String parameterName) throws SQLException { // 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 = paramNames.get(i); + String sParam = parameterNames.get(i); sParam = sParam.substring(1, sParam.length()); if (sParam.equals(columnNameWithoutAtSign)) { matchPos = i; @@ -1486,7 +1368,7 @@ public NClob getNClob(String parameterName) throws SQLException { // 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 = paramNames.get(i); + String sParam = parameterNames.get(i); sParam = sParam.substring(1, sParam.length()); if (sParam.equalsIgnoreCase(columnNameWithoutAtSign)) { matchPos = i; @@ -1500,6 +1382,7 @@ public NClob getNClob(String parameterName) throws SQLException { Object[] msgArgs = {columnName, procedureName}; SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), "07009", false); } + // @RETURN_VALUE is always in the list. If the user uses return value ?=call(@p1) syntax then // @p1 is index 2 otherwise its index 1. if (bReturnValueSyntax) // 3.2717 @@ -1508,130 +1391,76 @@ public NClob getNClob(String parameterName) throws SQLException { return matchPos; } - public void setTimestamp(String sCol, - java.sql.Timestamp x, - Calendar c) throws SQLServerException { + @Override + public void setTimestamp(String parameterName, + java.sql.Timestamp value, + Calendar calendar) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setTimeStamp", new Object[] {sCol, x, c}); + loggerExternal.entering(getClassNameLogging(), "setTimeStamp", new Object[] {parameterName, value, calendar}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TIMESTAMP, x, JavaType.TIMESTAMP, c, false); + setValue(findColumn(parameterName), JDBCType.TIMESTAMP, value, JavaType.TIMESTAMP, calendar, false); loggerExternal.exiting(getClassNameLogging(), "setTimeStamp"); } - /** - * Sets the designated parameter to the given java.sql.Timestamp value. The driver converts this to an SQL TIMESTAMP - * value when it sends it to the database. - * - * @param sCol - * the name of the parameter - * @param x - * the parameter value - * @param c - * a java.util.Calendar - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - * @see #getTimestamp - */ - public void setTimestamp(String sCol, - java.sql.Timestamp x, - Calendar c, + @Override + public void setTimestamp(String parameterName, + java.sql.Timestamp value, + Calendar calendar, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setTimeStamp", new Object[] {sCol, x, c, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setTimeStamp", new Object[] {parameterName, value, calendar, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TIMESTAMP, x, JavaType.TIMESTAMP, c, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.TIMESTAMP, value, JavaType.TIMESTAMP, calendar, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setTimeStamp"); } - public void setTime(String sCol, - java.sql.Time x, - Calendar c) throws SQLServerException { + @Override + public void setTime(String parameterName, + java.sql.Time value, + Calendar calendar) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {sCol, x, c}); + loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {parameterName, value, calendar}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TIME, x, JavaType.TIME, c, false); + setValue(findColumn(parameterName), JDBCType.TIME, value, JavaType.TIME, calendar, false); loggerExternal.exiting(getClassNameLogging(), "setTime"); } - /** - * Sets the designated parameter to the given java.sql.Time value, using the given Calendar object. The driver uses the - * Calendar object to construct an SQL TIME value, which the driver then sends to the database. With a a - * Calendar object, the driver can calculate the time taking into account a custom timezone. If no Calendar object is - * specified, the driver uses the default timezone, which is that of the virtual machine running the application. - * - * @param sCol - * the name of the parameter - * @param x - * the parameter value - * @param c - * the Calendar object the driver will use to construct the time - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - * @see #getTime - */ - public void setTime(String sCol, - java.sql.Time x, - Calendar c, + @Override + public void setTime(String parameterName, + java.sql.Time value, + Calendar calendar, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {sCol, x, c, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {parameterName, value, calendar, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TIME, x, JavaType.TIME, c, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.TIME, value, JavaType.TIME, calendar, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setTime"); } - public void setDate(String sCol, - java.sql.Date x, - Calendar c) throws SQLServerException { + @Override + public void setDate(String parameterName, + java.sql.Date value, + Calendar calendar) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setDate", new Object[] {sCol, x, c}); + loggerExternal.entering(getClassNameLogging(), "setDate", new Object[] {parameterName, value, calendar}); checkClosed(); - setValue(findColumn(sCol), JDBCType.DATE, x, JavaType.DATE, c, false); + setValue(findColumn(parameterName), JDBCType.DATE, value, JavaType.DATE, calendar, false); loggerExternal.exiting(getClassNameLogging(), "setDate"); } - /** - * Sets the designated parameter to the given java.sql.Date value, using the given Calendar object. The driver uses the - * Calendar object to construct an SQL DATE value, which the driver then sends to the database. With a a - * Calendar object, the driver can calculate the date taking into account a custom timezone. If no Calendar object is - * specified, the driver uses the default timezone, which is that of the virtual machine running the application. - * - * @param sCol - * the name of the parameter - * @param x - * the parameter value - * @param c - * the Calendar object the driver will use to construct the date - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - * @see #getDate - */ - public void setDate(String sCol, - java.sql.Date x, - Calendar c, + @Override + public void setDate(String parameterName, + java.sql.Date value, + Calendar calendar, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setDate", new Object[] {sCol, x, c, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setDate", new Object[] {parameterName, value, calendar, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.DATE, x, JavaType.DATE, c, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.DATE, value, JavaType.DATE, calendar, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setDate"); } + @Override public final void setCharacterStream(String parameterName, Reader reader) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1641,6 +1470,7 @@ public final void setCharacterStream(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setCharacterStream"); } + @Override public final void setCharacterStream(String parameterName, Reader value, int length) throws SQLException { @@ -1651,6 +1481,7 @@ public final void setCharacterStream(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setCharacterStream"); } + @Override public final void setCharacterStream(String parameterName, Reader reader, long length) throws SQLException { @@ -1662,6 +1493,7 @@ public final void setCharacterStream(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setCharacterStream"); } + @Override public final void setNCharacterStream(String parameterName, Reader value) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1671,6 +1503,7 @@ public final void setNCharacterStream(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setNCharacterStream"); } + @Override public final void setNCharacterStream(String parameterName, Reader value, long length) throws SQLException { @@ -1681,15 +1514,17 @@ public final void setNCharacterStream(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setNCharacterStream"); } + @Override public final void setClob(String parameterName, - Clob x) throws SQLException { + Clob value) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setClob", new Object[] {parameterName, x}); + loggerExternal.entering(getClassNameLogging(), "setClob", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.CLOB, x, JavaType.CLOB, false); + setValue(findColumn(parameterName), JDBCType.CLOB, value, JavaType.CLOB, false); loggerExternal.exiting(getClassNameLogging(), "setClob"); } + @Override public final void setClob(String parameterName, Reader reader) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1699,6 +1534,7 @@ public final void setClob(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setClob"); } + @Override public final void setClob(String parameterName, Reader value, long length) throws SQLException { @@ -1709,6 +1545,7 @@ public final void setClob(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setClob"); } + @Override public final void setNClob(String parameterName, NClob value) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1718,6 +1555,7 @@ public final void setNClob(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setNClob"); } + @Override public final void setNClob(String parameterName, Reader reader) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1727,6 +1565,7 @@ public final void setNClob(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setNClob"); } + @Override public final void setNClob(String parameterName, Reader reader, long length) throws SQLException { @@ -1737,6 +1576,7 @@ public final void setNClob(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setNClob"); } + @Override public final void setNString(String parameterName, String value) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1746,26 +1586,10 @@ public final void setNString(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setNString"); } - /** - * Sets the designated parameter to the given String object. The driver converts this to a SQL NCHAR or - * NVARCHAR or LONGNVARCHAR - * - * @param parameterName - * the name of the parameter to be set - * @param value - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLException - * if parameterName does not correspond to a named parameter; if the driver does not support national character sets; if the driver - * can detect that a data conversion error could occur; if a database access error occurs or this method is called on a closed - * CallableStatement - */ + @Override public final void setNString(String parameterName, String value, - boolean forceEncrypt) throws SQLException { + boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setNString", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); @@ -1773,131 +1597,77 @@ public final void setNString(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setNString"); } - public void setObject(String sCol, - Object o) throws SQLServerException { + @Override + public void setObject(String parameterName, + Object value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {sCol, o}); + loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {parameterName, value}); checkClosed(); - setObjectNoType(findColumn(sCol), o, false); + setObjectNoType(findColumn(parameterName), value, false); loggerExternal.exiting(getClassNameLogging(), "setObject"); } - public void setObject(String sCol, - Object o, - int n) throws SQLServerException { + @Override + public void setObject(String parameterName, + Object value, + int sqlType) throws SQLServerException { String tvpName = null; if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {sCol, o, n}); + loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {parameterName, value, sqlType}); checkClosed(); - if (microsoft.sql.Types.STRUCTURED == n) { - tvpName = getTVPNameIfNull(findColumn(sCol), null); - setObject(setterGetParam(findColumn(sCol)), o, JavaType.TVP, JDBCType.TVP, null, null, false, findColumn(sCol), tvpName); + if (microsoft.sql.Types.STRUCTURED == sqlType) { + tvpName = getTVPNameIfNull(findColumn(parameterName), null); + setObject(setterGetParam(findColumn(parameterName)), value, JavaType.TVP, JDBCType.TVP, null, null, false, findColumn(parameterName), + tvpName); } else - setObject(setterGetParam(findColumn(sCol)), o, JavaType.of(o), JDBCType.of(n), null, null, false, findColumn(sCol), tvpName); + setObject(setterGetParam(findColumn(parameterName)), value, JavaType.of(value), JDBCType.of(sqlType), null, null, false, + findColumn(parameterName), tvpName); loggerExternal.exiting(getClassNameLogging(), "setObject"); } - public void setObject(String sCol, - Object o, - int n, - int m) throws SQLServerException { + @Override + public void setObject(String parameterName, + Object value, + int sqlType, + int decimals) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {sCol, o, n, m}); + loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {parameterName, value, sqlType, decimals}); checkClosed(); - - setObject(setterGetParam(findColumn(sCol)), o, JavaType.of(o), JDBCType.of(n), m, null, false, findColumn(sCol), null); - + setObject(setterGetParam(findColumn(parameterName)), value, JavaType.of(value), JDBCType.of(sqlType), decimals, null, false, + findColumn(parameterName), null); loggerExternal.exiting(getClassNameLogging(), "setObject"); } - /** - * Sets the value of the designated parameter with the given object. - * - *

- * The given Java object will be converted to the given targetSqlType before being sent to the database. - * - * If the object has a custom mapping (is of a class implementing the interface SQLData), the JDBC driver should call the method - * SQLData.writeSQL to write it to the SQL data stream. If, on the other hand, the object is of a class implementing - * Ref, Blob, Clob, NClob, Struct, java.net.URL, or - * Array, the driver should pass it to the database as a value of the corresponding SQL type. - *

- * Note that this method may be used to pass datatabase- specific abstract data types. - * - * @param sCol - * the name of the parameter - * @param o - * the object containing the input parameter value - * @param n - * the SQL type (as defined in java.sql.Types) to be sent to the database. The scale argument may further qualify this type. - * @param m - * for java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types, this is the number of digits after the decimal point. For all other - * types, this value will be ignored. - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - * @see Types - * @see #getObject - */ - public void setObject(String sCol, - Object o, - int n, - int m, + @Override + public void setObject(String parameterName, + Object value, + int sqlType, + int decimals, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {sCol, o, n, m, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {parameterName, value, sqlType, decimals, forceEncrypt}); checkClosed(); // scale - for java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types, // this is the number of digits after the decimal point. // For all other types, this value will be ignored. - setObject(setterGetParam(findColumn(sCol)), o, JavaType.of(o), JDBCType.of(n), - (java.sql.Types.NUMERIC == n || java.sql.Types.DECIMAL == n) ? m : null, null, forceEncrypt, findColumn(sCol), null); + setObject(setterGetParam(findColumn(parameterName)), value, JavaType.of(value), JDBCType.of(sqlType), + (java.sql.Types.NUMERIC == sqlType || java.sql.Types.DECIMAL == sqlType) ? decimals : null, null, forceEncrypt, + findColumn(parameterName), null); loggerExternal.exiting(getClassNameLogging(), "setObject"); } - /** - * Sets the value of the designated parameter with the given object. - * - *

- * The given Java object will be converted to the given targetSqlType before being sent to the database. - * - * If the object has a custom mapping (is of a class implementing the interface SQLData), the JDBC driver should call the method - * SQLData.writeSQL to write it to the SQL data stream. If, on the other hand, the object is of a class implementing - * Ref, Blob, Clob, NClob, Struct, java.net.URL, or - * Array, the driver should pass it to the database as a value of the corresponding SQL type. - *

- * Note that this method may be used to pass datatabase- specific abstract data types. - * - * @param sCol - * the name of the parameter - * @param x - * the object containing the input parameter value - * @param targetSqlType - * the SQL type (as defined in java.sql.Types) to be sent to the database. The scale argument may further qualify this type. - * @param precision - * the precision of the column. - * @param scale - * the scale of the column. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - * @see Types - * @see #getObject - */ - public final void setObject(String sCol, - Object x, + @Override + public final void setObject(String parameterName, + Object value, int targetSqlType, Integer precision, int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {sCol, x, targetSqlType, precision, scale}); + loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {parameterName, value, targetSqlType, precision, scale}); checkClosed(); // scale - for java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types, @@ -1905,23 +1675,25 @@ public final void setObject(String sCol, // InputStream and Reader, this is the length of the data in the stream or reader. // For all other types, this value will be ignored. - setObject(setterGetParam(findColumn(sCol)), x, JavaType.of(x), - JDBCType.of(targetSqlType), (java.sql.Types.NUMERIC == targetSqlType || java.sql.Types.DECIMAL == targetSqlType - || InputStream.class.isInstance(x) || Reader.class.isInstance(x)) ? scale : null, - precision, false, findColumn(sCol), null); + setObject(setterGetParam(findColumn(parameterName)), value, JavaType.of(value), JDBCType.of(targetSqlType), + (java.sql.Types.NUMERIC == targetSqlType || java.sql.Types.DECIMAL == targetSqlType || InputStream.class.isInstance(value) + || Reader.class.isInstance(value)) ? scale : null, + precision, false, findColumn(parameterName), null); loggerExternal.exiting(getClassNameLogging(), "setObject"); } + @Override public final void setAsciiStream(String parameterName, - InputStream x) throws SQLException { + InputStream value) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setAsciiStream", new Object[] {parameterName, x}); + loggerExternal.entering(getClassNameLogging(), "setAsciiStream", new Object[] {parameterName, value}); checkClosed(); - setStream(findColumn(parameterName), StreamType.ASCII, x, JavaType.INPUTSTREAM, DataTypes.UNKNOWN_STREAM_LENGTH); + setStream(findColumn(parameterName), StreamType.ASCII, value, JavaType.INPUTSTREAM, DataTypes.UNKNOWN_STREAM_LENGTH); loggerExternal.exiting(getClassNameLogging(), "setAsciiStream"); } + @Override public final void setAsciiStream(String parameterName, InputStream value, int length) throws SQLException { @@ -1932,26 +1704,29 @@ public final void setAsciiStream(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setAsciiStream"); } + @Override public final void setAsciiStream(String parameterName, - InputStream x, + InputStream value, long length) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setAsciiStream", new Object[] {parameterName, x, length}); + loggerExternal.entering(getClassNameLogging(), "setAsciiStream", new Object[] {parameterName, value, length}); checkClosed(); - setStream(findColumn(parameterName), StreamType.ASCII, x, JavaType.INPUTSTREAM, length); + setStream(findColumn(parameterName), StreamType.ASCII, value, JavaType.INPUTSTREAM, length); loggerExternal.exiting(getClassNameLogging(), "setAsciiStream"); } + @Override public final void setBinaryStream(String parameterName, - InputStream x) throws SQLException { + InputStream value) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setBinaryStream", new Object[] {parameterName, x}); + loggerExternal.entering(getClassNameLogging(), "setBinaryStream", new Object[] {parameterName, value}); checkClosed(); - setStream(findColumn(parameterName), StreamType.BINARY, x, JavaType.INPUTSTREAM, DataTypes.UNKNOWN_STREAM_LENGTH); + setStream(findColumn(parameterName), StreamType.BINARY, value, JavaType.INPUTSTREAM, DataTypes.UNKNOWN_STREAM_LENGTH); loggerExternal.exiting(getClassNameLogging(), "setBinaryStream"); } + @Override public final void setBinaryStream(String parameterName, InputStream value, int length) throws SQLException { @@ -1962,16 +1737,18 @@ public final void setBinaryStream(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setBinaryStream"); } + @Override public final void setBinaryStream(String parameterName, - InputStream x, + InputStream value, long length) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setBinaryStream", new Object[] {parameterName, x, length}); + loggerExternal.entering(getClassNameLogging(), "setBinaryStream", new Object[] {parameterName, value, length}); checkClosed(); - setStream(findColumn(parameterName), StreamType.BINARY, x, JavaType.INPUTSTREAM, length); + setStream(findColumn(parameterName), StreamType.BINARY, value, JavaType.INPUTSTREAM, length); loggerExternal.exiting(getClassNameLogging(), "setBinaryStream"); } + @Override public final void setBlob(String parameterName, Blob inputStream) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1981,9 +1758,9 @@ public final void setBlob(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setBlob"); } + @Override public final void setBlob(String parameterName, InputStream value) throws SQLException { - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBlob", new Object[] {parameterName, value}); checkClosed(); @@ -1991,6 +1768,7 @@ public final void setBlob(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setBlob"); } + @Override public final void setBlob(String parameterName, InputStream inputStream, long length) throws SQLException { @@ -2001,928 +1779,525 @@ public final void setBlob(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setBlob"); } - public void setTimestamp(String sCol, - java.sql.Timestamp t) throws SQLServerException { + @Override + public void setTimestamp(String parameterName, + java.sql.Timestamp value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {sCol, t}); + loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TIMESTAMP, t, JavaType.TIMESTAMP, false); + setValue(findColumn(parameterName), JDBCType.TIMESTAMP, value, JavaType.TIMESTAMP, false); loggerExternal.exiting(getClassNameLogging(), "setTimestamp"); } - /** - * Sets the designated parameter to the given java.sql.Timestamp value. The driver converts this to an SQL TIMESTAMP - * value when it sends it to the database. - * - * @param sCol - * the name of the parameter - * @param t - * the parameter value - * @param scale - * the scale of the parameter - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - * @see #getTimestamp - */ - public void setTimestamp(String sCol, - java.sql.Timestamp t, + @Override + public void setTimestamp(String parameterName, + java.sql.Timestamp value, int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {sCol, t}); + loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TIMESTAMP, t, JavaType.TIMESTAMP, null, scale, false); + setValue(findColumn(parameterName), JDBCType.TIMESTAMP, value, JavaType.TIMESTAMP, null, scale, false); loggerExternal.exiting(getClassNameLogging(), "setTimestamp"); } - /** - * Sets the designated parameter to the given java.sql.Timestamp value. The driver converts this to an SQL TIMESTAMP - * value when it sends it to the database. - * - * @param sCol - * the name of the parameter - * @param t - * the parameter value - * @param scale - * the scale of the parameter - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - * @see #getTimestamp - */ - public void setTimestamp(String sCol, - java.sql.Timestamp t, + @Override + public void setTimestamp(String parameterName, + java.sql.Timestamp value, int scale, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {sCol, t, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TIMESTAMP, t, JavaType.TIMESTAMP, null, scale, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.TIMESTAMP, value, JavaType.TIMESTAMP, null, scale, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setTimestamp"); } - public void setDateTimeOffset(String sCol, - microsoft.sql.DateTimeOffset t) throws SQLException { + @Override + public void setDateTimeOffset(String parameterName, + microsoft.sql.DateTimeOffset value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {sCol, t}); + loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.DATETIMEOFFSET, t, JavaType.DATETIMEOFFSET, false); + setValue(findColumn(parameterName), JDBCType.DATETIMEOFFSET, value, JavaType.DATETIMEOFFSET, false); loggerExternal.exiting(getClassNameLogging(), "setDateTimeOffset"); } - /** - * Sets parameter parameterName to DateTimeOffset x - * - * @param sCol - * the name of the parameter - * @param t - * DateTimeOffset value - * @param scale - * the scale of the parameter - * @throws SQLException - * if an error occurs - */ - public void setDateTimeOffset(String sCol, - microsoft.sql.DateTimeOffset t, - int scale) throws SQLException { + @Override + public void setDateTimeOffset(String parameterName, + microsoft.sql.DateTimeOffset value, + int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {sCol, t}); + loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.DATETIMEOFFSET, t, JavaType.DATETIMEOFFSET, null, scale, false); + setValue(findColumn(parameterName), JDBCType.DATETIMEOFFSET, value, JavaType.DATETIMEOFFSET, null, scale, false); loggerExternal.exiting(getClassNameLogging(), "setDateTimeOffset"); } - /** - * Sets parameter parameterName to DateTimeOffset x - * - * @param sCol - * the name of the parameter - * @param t - * DateTimeOffset value - * @param scale - * the scale of the parameter - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLException - * if an error occurs - */ - public void setDateTimeOffset(String sCol, - microsoft.sql.DateTimeOffset t, + @Override + public void setDateTimeOffset(String parameterName, + microsoft.sql.DateTimeOffset value, int scale, - boolean forceEncrypt) throws SQLException { + boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {sCol, t, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.DATETIMEOFFSET, t, JavaType.DATETIMEOFFSET, null, scale, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.DATETIMEOFFSET, value, JavaType.DATETIMEOFFSET, null, scale, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setDateTimeOffset"); } - public void setDate(String sCol, - java.sql.Date d) throws SQLServerException { + @Override + public void setDate(String parameterName, + java.sql.Date value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setDate", new Object[] {sCol, d}); + loggerExternal.entering(getClassNameLogging(), "setDate", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.DATE, d, JavaType.DATE, false); + setValue(findColumn(parameterName), JDBCType.DATE, value, JavaType.DATE, false); loggerExternal.exiting(getClassNameLogging(), "setDate"); } - public void setTime(String sCol, - java.sql.Time t) throws SQLServerException { + @Override + public void setTime(String parameterName, + java.sql.Time value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {sCol, t}); + loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TIME, t, JavaType.TIME, false); + setValue(findColumn(parameterName), JDBCType.TIME, value, JavaType.TIME, false); loggerExternal.exiting(getClassNameLogging(), "setTime"); } - /** - * Sets the designated parameter to the given java.sql.Time value. The driver converts this to an SQL TIME value when it - * sends it to the database. - * - * @param sCol - * the name of the parameter - * @param t - * the parameter value - * @param scale - * the scale of the column - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - * @see #getTime - */ - public void setTime(String sCol, - java.sql.Time t, + @Override + public void setTime(String parameterName, + java.sql.Time value, int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {sCol, t}); + loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TIME, t, JavaType.TIME, null, scale, false); + setValue(findColumn(parameterName), JDBCType.TIME, value, JavaType.TIME, null, scale, false); loggerExternal.exiting(getClassNameLogging(), "setTime"); } - /** - * Sets the designated parameter to the given java.sql.Time value. The driver converts this to an SQL TIME value when it - * sends it to the database. - * - * @param sCol - * the name of the parameter - * @param t - * the parameter value - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - * @see #getTime - */ - public void setTime(String sCol, - java.sql.Time t, + @Override + public void setTime(String parameterName, + java.sql.Time value, int scale, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {sCol, t, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TIME, t, JavaType.TIME, null, scale, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.TIME, value, JavaType.TIME, null, scale, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setTime"); } - /** - * Sets the designated parameter to the given java.sql.Timestamp value. The driver converts this to an SQL DATETIME - * value when it sends it to the database. - * - * @param sCol - * the name of the parameter - * @param x - * the parameter value - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setDateTime(String sCol, - java.sql.Timestamp x) throws SQLServerException { + @Override + public void setDateTime(String parameterName, + java.sql.Timestamp value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setDateTime", new Object[] {sCol, x}); + loggerExternal.entering(getClassNameLogging(), "setDateTime", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.DATETIME, x, JavaType.TIMESTAMP, false); + setValue(findColumn(parameterName), JDBCType.DATETIME, value, JavaType.TIMESTAMP, false); loggerExternal.exiting(getClassNameLogging(), "setDateTime"); } - /** - * Sets the designated parameter to the given java.sql.Timestamp value. The driver converts this to an SQL DATETIME - * value when it sends it to the database. - * - * @param sCol - * the name of the parameter - * @param x - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setDateTime(String sCol, - java.sql.Timestamp x, + @Override + public void setDateTime(String parameterName, + java.sql.Timestamp value, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setDateTime", new Object[] {sCol, x, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setDateTime", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.DATETIME, x, JavaType.TIMESTAMP, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.DATETIME, value, JavaType.TIMESTAMP, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setDateTime"); } - /** - * Sets the designated parameter to the given java.sql.Timestamp value. The driver converts this to an SQL SMALLDATETIME - * value when it sends it to the database. - * - * @param sCol - * the name of the parameter - * @param x - * the parameter value - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setSmallDateTime(String sCol, - java.sql.Timestamp x) throws SQLServerException { + @Override + public void setSmallDateTime(String parameterName, + java.sql.Timestamp value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setSmallDateTime", new Object[] {sCol, x}); + loggerExternal.entering(getClassNameLogging(), "setSmallDateTime", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.SMALLDATETIME, x, JavaType.TIMESTAMP, false); + setValue(findColumn(parameterName), JDBCType.SMALLDATETIME, value, JavaType.TIMESTAMP, false); loggerExternal.exiting(getClassNameLogging(), "setSmallDateTime"); } - /** - * Sets the designated parameter to the given java.sql.Timestamp value. The driver converts this to an SQL SMALLDATETIME - * value when it sends it to the database. - * - * @param sCol - * the name of the parameter - * @param x - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setSmallDateTime(String sCol, - java.sql.Timestamp x, + @Override + public void setSmallDateTime(String parameterName, + java.sql.Timestamp value, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setSmallDateTime", new Object[] {sCol, x, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setSmallDateTime", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.SMALLDATETIME, x, JavaType.TIMESTAMP, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.SMALLDATETIME, value, JavaType.TIMESTAMP, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setSmallDateTime"); } - /** - * Sets the designated parameter to the given String value. The driver converts this to an SQL uniqueIdentifier value - * when it sends it to the database. - * - * @param sCol - * the name of the parameter - * @param guid - * the parameter value - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setUniqueIdentifier(String sCol, + @Override + public void setUniqueIdentifier(String parameterName, String guid) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setUniqueIdentifier", new Object[] {sCol, guid}); + loggerExternal.entering(getClassNameLogging(), "setUniqueIdentifier", new Object[] {parameterName, guid}); checkClosed(); - setValue(findColumn(sCol), JDBCType.GUID, guid, JavaType.STRING, false); + setValue(findColumn(parameterName), JDBCType.GUID, guid, JavaType.STRING, false); loggerExternal.exiting(getClassNameLogging(), "setUniqueIdentifier"); } - /** - * Sets the designated parameter to the given String value. The driver converts this to an SQL uniqueIdentifier value - * when it sends it to the database. - * - * @param sCol - * the name of the parameter - * @param guid - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setUniqueIdentifier(String sCol, + @Override + public void setUniqueIdentifier(String parameterName, String guid, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setUniqueIdentifier", new Object[] {sCol, guid, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setUniqueIdentifier", new Object[] {parameterName, guid, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.GUID, guid, JavaType.STRING, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.GUID, guid, JavaType.STRING, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setUniqueIdentifier"); } - public void setBytes(String sCol, - byte[] b) throws SQLServerException { + @Override + public void setBytes(String parameterName, + byte[] value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setBytes", new Object[] {sCol, b}); + loggerExternal.entering(getClassNameLogging(), "setBytes", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.BINARY, b, JavaType.BYTEARRAY, false); + setValue(findColumn(parameterName), JDBCType.BINARY, value, JavaType.BYTEARRAY, false); loggerExternal.exiting(getClassNameLogging(), "setBytes"); } - /** - * Sets the designated parameter to the given Java array of bytes. The driver converts this to an SQL VARBINARY or - * LONGVARBINARY (depending on the argument's size relative to the driver's limits on VARBINARY values) when it sends it - * to the database. - * - * @param sCol - * the name of the parameter - * @param b - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setBytes(String sCol, - byte[] b, + @Override + public void setBytes(String parameterName, + byte[] value, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setBytes", new Object[] {sCol, b, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setBytes", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.BINARY, b, JavaType.BYTEARRAY, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.BINARY, value, JavaType.BYTEARRAY, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setBytes"); } - public void setByte(String sCol, - byte b) throws SQLServerException { + @Override + public void setByte(String parameterName, + byte value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setByte", new Object[] {sCol, b}); + loggerExternal.entering(getClassNameLogging(), "setByte", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TINYINT, b, JavaType.BYTE, false); + setValue(findColumn(parameterName), JDBCType.TINYINT, value, JavaType.BYTE, false); loggerExternal.exiting(getClassNameLogging(), "setByte"); } - /** - * Sets the designated parameter to the given Java byte value. The driver converts this to an SQL TINYINT value when it - * sends it to the database. - * - * @param sCol - * the name of the parameter - * @param b - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setByte(String sCol, - byte b, + @Override + public void setByte(String parameterName, + byte value, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setByte", new Object[] {sCol, b, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setByte", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TINYINT, b, JavaType.BYTE, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.TINYINT, value, JavaType.BYTE, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setByte"); } - public void setString(String sCol, - String s) throws SQLServerException { + @Override + public void setString(String parameterName, + String value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setString", new Object[] {sCol, s}); + loggerExternal.entering(getClassNameLogging(), "setString", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.VARCHAR, s, JavaType.STRING, false); + setValue(findColumn(parameterName), JDBCType.VARCHAR, value, JavaType.STRING, false); loggerExternal.exiting(getClassNameLogging(), "setString"); } - /** - * Sets the designated parameter to the given Java String value. The driver converts this to an SQL VARCHAR or - * LONGVARCHAR value (depending on the argument's size relative to the driver's limits on VARCHAR values) when it sends - * it to the database. - * - * @param sCol - * the name of the parameter - * @param s - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setString(String sCol, - String s, + @Override + public void setString(String parameterName, + String value, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setString", new Object[] {sCol, s, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setString", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.VARCHAR, s, JavaType.STRING, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.VARCHAR, value, JavaType.STRING, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setString"); } - /** - * Sets the designated parameter to the given Java java.math.BigDecimal value. The driver converts this to an SQL Money - * value. - * - * @param sCol - * the name of the parameter - * @param bd - * the parameter value - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setMoney(String sCol, - BigDecimal bd) throws SQLServerException { + @Override + public void setMoney(String parameterName, + BigDecimal value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setMoney", new Object[] {sCol, bd}); + loggerExternal.entering(getClassNameLogging(), "setMoney", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.MONEY, bd, JavaType.BIGDECIMAL, false); + setValue(findColumn(parameterName), JDBCType.MONEY, value, JavaType.BIGDECIMAL, false); loggerExternal.exiting(getClassNameLogging(), "setMoney"); } - /** - * Sets the designated parameter to the given Java java.math.BigDecimal value. The driver converts this to an SQL Money - * value. - * - * @param sCol - * the name of the parameter - * @param bd - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setMoney(String sCol, - BigDecimal bd, + @Override + public void setMoney(String parameterName, + BigDecimal value, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setMoney", new Object[] {sCol, bd, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setMoney", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.MONEY, bd, JavaType.BIGDECIMAL, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.MONEY, value, JavaType.BIGDECIMAL, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setMoney"); } - /** - * Sets the designated parameter to the given Java java.math.BigDecimal value. The driver converts this to an SQL - * smallMoney value. - * - * @param sCol - * the name of the parameter - * @param bd - * the parameter value - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setSmallMoney(String sCol, - BigDecimal bd) throws SQLServerException { + @Override + public void setSmallMoney(String parameterName, + BigDecimal value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setSmallMoney", new Object[] {sCol, bd}); + loggerExternal.entering(getClassNameLogging(), "setSmallMoney", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.SMALLMONEY, bd, JavaType.BIGDECIMAL, false); + setValue(findColumn(parameterName), JDBCType.SMALLMONEY, value, JavaType.BIGDECIMAL, false); loggerExternal.exiting(getClassNameLogging(), "setSmallMoney"); } - /** - * Sets the designated parameter to the given Java java.math.BigDecimal value. The driver converts this to an SQL - * smallMoney value. - * - * @param sCol - * the name of the parameter - * @param bd - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setSmallMoney(String sCol, - BigDecimal bd, + @Override + public void setSmallMoney(String parameterName, + BigDecimal value, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setSmallMoney", new Object[] {sCol, bd, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setSmallMoney", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.SMALLMONEY, bd, JavaType.BIGDECIMAL, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.SMALLMONEY, value, JavaType.BIGDECIMAL, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setSmallMoney"); } - public void setBigDecimal(String sCol, - BigDecimal bd) throws SQLServerException { + @Override + public void setBigDecimal(String parameterName, + BigDecimal value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {sCol, bd}); + loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.DECIMAL, bd, JavaType.BIGDECIMAL, false); + setValue(findColumn(parameterName), JDBCType.DECIMAL, value, JavaType.BIGDECIMAL, false); loggerExternal.exiting(getClassNameLogging(), "setBigDecimal"); } - /** - * Sets the designated parameter to the given java.math.BigDecimal value. The driver converts this to an SQL NUMERIC - * value when it sends it to the database. - * - * @param sCol - * the name of the parameter - * @param bd - * the parameter value - * @param precision - * the precision of the column - * @param scale - * the scale of the column - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setBigDecimal(String sCol, - BigDecimal bd, + @Override + public void setBigDecimal(String parameterName, + BigDecimal value, int precision, int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {sCol, bd, precision, scale}); + loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {parameterName, value, precision, scale}); checkClosed(); - setValue(findColumn(sCol), JDBCType.DECIMAL, bd, JavaType.BIGDECIMAL, precision, scale, false); + setValue(findColumn(parameterName), JDBCType.DECIMAL, value, JavaType.BIGDECIMAL, precision, scale, false); loggerExternal.exiting(getClassNameLogging(), "setBigDecimal"); } - /** - * Sets the designated parameter to the given java.math.BigDecimal value. The driver converts this to an SQL NUMERIC - * value when it sends it to the database. - * - * @param sCol - * the name of the parameter - * @param bd - * the parameter value - * @param precision - * the precision of the column - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setBigDecimal(String sCol, - BigDecimal bd, + @Override + public void setBigDecimal(String parameterName, + BigDecimal value, int precision, int scale, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {sCol, bd, precision, scale, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {parameterName, value, precision, scale, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.DECIMAL, bd, JavaType.BIGDECIMAL, precision, scale, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.DECIMAL, value, JavaType.BIGDECIMAL, precision, scale, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setBigDecimal"); } - public void setDouble(String sCol, - double d) throws SQLServerException { + @Override + public void setDouble(String parameterName, + double value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setDouble", new Object[] {sCol, d}); + loggerExternal.entering(getClassNameLogging(), "setDouble", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.DOUBLE, d, JavaType.DOUBLE, false); + setValue(findColumn(parameterName), JDBCType.DOUBLE, value, JavaType.DOUBLE, false); loggerExternal.exiting(getClassNameLogging(), "setDouble"); } - /** - * Sets the designated parameter to the given Java double value. The driver converts this to an SQL DOUBLE value when it - * sends it to the database. - * - * @param sCol - * the name of the parameter - * @param d - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setDouble(String sCol, - double d, + @Override + public void setDouble(String parameterName, + double value, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setDouble", new Object[] {sCol, d, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setDouble", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.DOUBLE, d, JavaType.DOUBLE, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.DOUBLE, value, JavaType.DOUBLE, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setDouble"); } - public void setFloat(String sCol, - float f) throws SQLServerException { + @Override + public void setFloat(String parameterName, + float value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setFloat", new Object[] {sCol, f}); + loggerExternal.entering(getClassNameLogging(), "setFloat", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.REAL, f, JavaType.FLOAT, false); + setValue(findColumn(parameterName), JDBCType.REAL, value, JavaType.FLOAT, false); loggerExternal.exiting(getClassNameLogging(), "setFloat"); } - /** - * Sets the designated parameter to the given Java float value. The driver converts this to an SQL FLOAT value when it - * sends it to the database. - * - * @param sCol - * the name of the parameter - * @param f - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setFloat(String sCol, - float f, + @Override + public void setFloat(String parameterName, + float value, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setFloat", new Object[] {sCol, f, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setFloat", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.REAL, f, JavaType.FLOAT, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.REAL, value, JavaType.FLOAT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setFloat"); } - public void setInt(String sCol, - int i) throws SQLServerException { + @Override + public void setInt(String parameterName, + int value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setInt", new Object[] {sCol, i}); + loggerExternal.entering(getClassNameLogging(), "setInt", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.INTEGER, i, JavaType.INTEGER, false); + setValue(findColumn(parameterName), JDBCType.INTEGER, value, JavaType.INTEGER, false); loggerExternal.exiting(getClassNameLogging(), "setInt"); } - /** - * Sets the designated parameter to the given Java int value. The driver converts this to an SQL INTEGER value when it - * sends it to the database. - * - * @param sCol - * the name of the parameter - * @param i - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setInt(String sCol, - int i, + @Override + public void setInt(String parameterName, + int value, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setInt", new Object[] {sCol, i, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setInt", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.INTEGER, i, JavaType.INTEGER, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.INTEGER, value, JavaType.INTEGER, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setInt"); } - public void setLong(String sCol, - long l) throws SQLServerException { + @Override + public void setLong(String parameterName, + long value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setLong", new Object[] {sCol, l}); + loggerExternal.entering(getClassNameLogging(), "setLong", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.BIGINT, l, JavaType.LONG, false); + setValue(findColumn(parameterName), JDBCType.BIGINT, value, JavaType.LONG, false); loggerExternal.exiting(getClassNameLogging(), "setLong"); } - /** - * Sets the designated parameter to the given Java long value. The driver converts this to an SQL BIGINT value when it - * sends it to the database. - * - * @param sCol - * the name of the parameter - * @param l - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setLong(String sCol, - long l, + @Override + public void setLong(String parameterName, + long value, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setLong", new Object[] {sCol, l, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setLong", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.BIGINT, l, JavaType.LONG, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.BIGINT, value, JavaType.LONG, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setLong"); } - public void setShort(String sCol, - short s) throws SQLServerException { + @Override + public void setShort(String parameterName, + short value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setShort", new Object[] {sCol, s}); + loggerExternal.entering(getClassNameLogging(), "setShort", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.SMALLINT, s, JavaType.SHORT, false); + setValue(findColumn(parameterName), JDBCType.SMALLINT, value, JavaType.SHORT, false); loggerExternal.exiting(getClassNameLogging(), "setShort"); } - /** - * Sets the designated parameter to the given Java short value. The driver converts this to an SQL SMALLINT value when - * it sends it to the database. - * - * @param sCol - * the name of the parameter - * @param s - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setShort(String sCol, - short s, + @Override + public void setShort(String parameterName, + short value, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setShort", new Object[] {sCol, s, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setShort", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.SMALLINT, s, JavaType.SHORT, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.SMALLINT, value, JavaType.SHORT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setShort"); } - public void setBoolean(String sCol, - boolean b) throws SQLServerException { + @Override + public void setBoolean(String parameterName, + boolean value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setBoolean", new Object[] {sCol, b}); + loggerExternal.entering(getClassNameLogging(), "setBoolean", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(sCol), JDBCType.BIT, b, JavaType.BOOLEAN, false); + setValue(findColumn(parameterName), JDBCType.BIT, value, JavaType.BOOLEAN, false); loggerExternal.exiting(getClassNameLogging(), "setBoolean"); } - /** - * Sets the designated parameter to the given Java boolean value. The driver converts this to an SQL BIT or - * BOOLEAN value when it sends it to the database. - * - * @param sCol - * the name of the parameter - * @param b - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * if parameterName does not correspond to a named parameter; if a database access error occurs or this method is called on a closed - * CallableStatement - */ - public void setBoolean(String sCol, - boolean b, + @Override + public void setBoolean(String parameterName, + boolean value, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setBoolean", new Object[] {sCol, b, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setBoolean", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.BIT, b, JavaType.BOOLEAN, forceEncrypt); + setValue(findColumn(parameterName), JDBCType.BIT, value, JavaType.BOOLEAN, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setBoolean"); } - public void setNull(String sCol, + @Override + public void setNull(String parameterName, int nType) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setNull", new Object[] {sCol, nType}); + loggerExternal.entering(getClassNameLogging(), "setNull", new Object[] {parameterName, nType}); checkClosed(); - setObject(setterGetParam(findColumn(sCol)), null, JavaType.OBJECT, JDBCType.of(nType), null, null, false, findColumn(sCol), null); + setObject(setterGetParam(findColumn(parameterName)), null, JavaType.OBJECT, JDBCType.of(nType), null, null, false, findColumn(parameterName), + null); loggerExternal.exiting(getClassNameLogging(), "setNull"); } - public void setNull(String sCol, + @Override + public void setNull(String parameterName, int nType, String sTypeName) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setNull", new Object[] {sCol, nType, sTypeName}); + loggerExternal.entering(getClassNameLogging(), "setNull", new Object[] {parameterName, nType, sTypeName}); checkClosed(); - setObject(setterGetParam(findColumn(sCol)), null, JavaType.OBJECT, JDBCType.of(nType), null, null, false, findColumn(sCol), sTypeName); + setObject(setterGetParam(findColumn(parameterName)), null, JavaType.OBJECT, JDBCType.of(nType), null, null, false, findColumn(parameterName), + sTypeName); loggerExternal.exiting(getClassNameLogging(), "setNull"); } - public void setURL(String sCol, - URL u) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "setURL", sCol); + @Override + public void setURL(String parameterName, + URL url) throws SQLException { + loggerExternal.entering(getClassNameLogging(), "setURL", parameterName); checkClosed(); - setURL(findColumn(sCol), u); + setURL(findColumn(parameterName), url); loggerExternal.exiting(getClassNameLogging(), "setURL"); } - /** - * Populates a table valued parameter passed to a stored procedure with a data table. - * - * @param sCol - * the name of the parameter - * @param tvpName - * the name of the type TVP - * @param tvpDataTable - * the data table object - * @throws SQLServerException - * when an error occurs - */ - public final void setStructured(String sCol, + @Override + public final void setStructured(String parameterName, String tvpName, SQLServerDataTable tvpDataTable) throws SQLServerException { - tvpName = getTVPNameIfNull(findColumn(sCol), tvpName); + tvpName = getTVPNameIfNull(findColumn(parameterName), tvpName); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setStructured", new Object[] {sCol, tvpName, tvpDataTable}); + loggerExternal.entering(getClassNameLogging(), "setStructured", new Object[] {parameterName, tvpName, tvpDataTable}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TVP, tvpDataTable, JavaType.TVP, tvpName); + setValue(findColumn(parameterName), JDBCType.TVP, tvpDataTable, JavaType.TVP, tvpName); loggerExternal.exiting(getClassNameLogging(), "setStructured"); } - /** - * Populates a table valued parameter passed to a stored procedure with a ResultSet retrieved from another table - * - * @param sCol - * the name of the parameter - * @param tvpName - * the name of the type TVP - * @param tvpResultSet - * the source result set object - * @throws SQLServerException - * when an error occurs - */ - public final void setStructured(String sCol, + @Override + public final void setStructured(String parameterName, String tvpName, ResultSet tvpResultSet) throws SQLServerException { - tvpName = getTVPNameIfNull(findColumn(sCol), tvpName); + tvpName = getTVPNameIfNull(findColumn(parameterName), tvpName); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setStructured", new Object[] {sCol, tvpName, tvpResultSet}); + loggerExternal.entering(getClassNameLogging(), "setStructured", new Object[] {parameterName, tvpName, tvpResultSet}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TVP, tvpResultSet, JavaType.TVP, tvpName); + setValue(findColumn(parameterName), JDBCType.TVP, tvpResultSet, JavaType.TVP, tvpName); loggerExternal.exiting(getClassNameLogging(), "setStructured"); } - /** - * Populates a table valued parameter passed to a stored procedure with an ISQLServerDataRecord object. - * - * @param sCol - * the name of the parameter - * @param tvpName - * the name of the type TVP - * @param tvpDataRecord - * ISQLServerDataRecord is used for streaming data and the user decides how to use it. tvpDataRecord is an ISQLServerDataRecord - * object.the source result set object - * @throws SQLServerException - * when an error occurs - */ - public final void setStructured(String sCol, + @Override + public final void setStructured(String parameterName, String tvpName, ISQLServerDataRecord tvpDataRecord) throws SQLServerException { - tvpName = getTVPNameIfNull(findColumn(sCol), tvpName); + tvpName = getTVPNameIfNull(findColumn(parameterName), tvpName); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setStructured", new Object[] {sCol, tvpName, tvpDataRecord}); + loggerExternal.entering(getClassNameLogging(), "setStructured", new Object[] {parameterName, tvpName, tvpDataRecord}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TVP, tvpDataRecord, JavaType.TVP, tvpName); + setValue(findColumn(parameterName), JDBCType.TVP, tvpDataRecord, JavaType.TVP, tvpName); loggerExternal.exiting(getClassNameLogging(), "setStructured"); } - public URL getURL(int n) throws SQLServerException { - NotImplemented(); + @Override + public URL getURL(int parameterIndex) throws SQLException { + SQLServerException.throwNotSupportedException(connection, this); return null; } - public URL getURL(String s) throws SQLServerException { - NotImplemented(); + @Override + public URL getURL(String parameterName) throws SQLException { + SQLServerException.throwNotSupportedException(connection, this); return null; } + @Override public final void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2932,6 +2307,7 @@ public final void setSQLXML(String parameterName, loggerExternal.exiting(getClassNameLogging(), "setSQLXML"); } + @Override public final SQLXML getSQLXML(int parameterIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getSQLXML", parameterIndex); checkClosed(); @@ -2940,6 +2316,7 @@ public final SQLXML getSQLXML(int parameterIndex) throws SQLException { return value; } + @Override public final SQLXML getSQLXML(String parameterName) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getSQLXML", parameterName); checkClosed(); @@ -2948,65 +2325,211 @@ public final SQLXML getSQLXML(String parameterName) throws SQLException { return value; } + @Override public final void setRowId(String parameterName, - RowId x) throws SQLException { - - // Not implemented - throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + RowId value) throws SQLException { + SQLServerException.throwNotSupportedException(connection, this); } + @Override public final RowId getRowId(int parameterIndex) throws SQLException { - - // Not implemented - throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + SQLServerException.throwNotSupportedException(connection, this); + return null; } + @Override public final RowId getRowId(String parameterName) throws SQLException { - - // Not implemented - throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + SQLServerException.throwNotSupportedException(connection, this); + return null; } - public void registerOutParameter(String s, - int n, - String s1) throws SQLServerException { + @Override + public void registerOutParameter(String parameterName, + int sqlType, + String typeName) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {s, n, s1}); + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType, typeName}); checkClosed(); - registerOutParameter(findColumn(s), n, s1); + registerOutParameter(findColumn(parameterName), sqlType, typeName); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } + @Override public void registerOutParameter(String parameterName, int sqlType, int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", - new Object[] {parameterName, sqlType, scale}); + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType, scale}); checkClosed(); registerOutParameter(findColumn(parameterName), sqlType, scale); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } + @Override public void registerOutParameter(String parameterName, int sqlType, int precision, int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", - new Object[] {parameterName, sqlType, scale}); + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType, scale}); checkClosed(); registerOutParameter(findColumn(parameterName), sqlType, precision, scale); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } - public void registerOutParameter(String s, - int n) throws SQLServerException { + @Override + public void registerOutParameter(String parameterName, + int sqlType) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {s, n}); + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType}); checkClosed(); - registerOutParameter(findColumn(s), n); + registerOutParameter(findColumn(parameterName), sqlType); + loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); + } + + @Override + public void registerOutParameter(int paramterIndex, + SQLType sqlType) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {paramterIndex, sqlType}); + + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + registerOutParameter(paramterIndex, sqlType.getVendorTypeNumber()); + loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); + } + + @Override + public void registerOutParameter(int paramterIndex, + SQLType sqlType, + String typeName) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {paramterIndex, sqlType, typeName}); + + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + registerOutParameter(paramterIndex, sqlType.getVendorTypeNumber(), typeName); + loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); + } + + @Override + public void registerOutParameter(int paramterIndex, + SQLType sqlType, + int scale) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {paramterIndex, sqlType, scale}); + + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + registerOutParameter(paramterIndex, sqlType.getVendorTypeNumber(), scale); + loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); + } + + @Override + public void registerOutParameter(int paramterIndex, + SQLType sqlType, + int precision, + int scale) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {paramterIndex, sqlType, scale}); + + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + registerOutParameter(paramterIndex, sqlType.getVendorTypeNumber(), precision, scale); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } + @Override + public void setObject(String parameterName, + Object value, + SQLType jdbcType) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {parameterName, value, jdbcType}); + + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + setObject(parameterName, value, jdbcType.getVendorTypeNumber()); + loggerExternal.exiting(getClassNameLogging(), "setObject"); + } + + @Override + public void setObject(String parameterName, + Object value, + SQLType jdbcType, + int scale) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {parameterName, value, jdbcType, scale}); + + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + setObject(parameterName, value, jdbcType.getVendorTypeNumber(), scale); + loggerExternal.exiting(getClassNameLogging(), "setObject"); + } + + @Override + public void setObject(String parameterName, + Object value, + SQLType jdbcType, + int scale, + boolean forceEncrypt) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {parameterName, value, jdbcType, scale, forceEncrypt}); + + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + setObject(parameterName, value, jdbcType.getVendorTypeNumber(), scale, forceEncrypt); + loggerExternal.exiting(getClassNameLogging(), "setObject"); + } + + @Override + public void registerOutParameter(String parameterName, + SQLType sqlType, + String typeName) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType, typeName}); + + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + registerOutParameter(parameterName, sqlType.getVendorTypeNumber(), typeName); + loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); + } + + @Override + public void registerOutParameter(String parameterName, + SQLType sqlType, + int scale) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType, scale}); + + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + registerOutParameter(parameterName, sqlType.getVendorTypeNumber(), scale); + loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); + } + + @Override + public void registerOutParameter(String parameterName, + SQLType sqlType, + int precision, + int scale) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType, scale}); + + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + registerOutParameter(parameterName, sqlType.getVendorTypeNumber(), precision, scale); + loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); + } + + @Override + public void registerOutParameter(String parameterName, + SQLType sqlType) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType}); + + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + registerOutParameter(parameterName, sqlType.getVendorTypeNumber()); + loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); + } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement42.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement42.java deleted file mode 100644 index cf5e1f246..000000000 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement42.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.jdbc; - -import java.sql.SQLType; - -/** - * - * This class is separated from SQLServerCallableStatement class in order to resolve compiling error of missing Java 8 Types when running with Java 7. - * - * This class will be initialized instead of SQLServerCallableStatement when Java 8 and JDBC 4.2 are used. - * - * It shares the same PreparedStatement implementation with SQLServerPreparedStatement42. - * - */ -public class SQLServerCallableStatement42 extends SQLServerCallableStatement implements ISQLServerCallableStatement42 { - - SQLServerCallableStatement42(SQLServerConnection connection, - String sql, - int nRSType, - int nRSConcur, - SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { - super(connection, sql, nRSType, nRSConcur, stmtColEncSetting); - } - - public void registerOutParameter(int index, - SQLType sqlType) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {index, sqlType}); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - registerOutParameter(index, sqlType.getVendorTypeNumber()); - loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); - } - - public void registerOutParameter(int index, - SQLType sqlType, - String typeName) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {index, sqlType, typeName}); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - registerOutParameter(index, sqlType.getVendorTypeNumber(), typeName); - - loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); - } - - public void registerOutParameter(int index, - SQLType sqlType, - int scale) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {index, sqlType, scale}); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - registerOutParameter(index, sqlType.getVendorTypeNumber(), scale); - - loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); - } - - public void registerOutParameter(int index, - SQLType sqlType, - int precision, - int scale) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {index, sqlType, scale}); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - registerOutParameter(index, sqlType.getVendorTypeNumber(), precision, scale); - - loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); - } - - public void setObject(String sCol, - Object obj, - SQLType jdbcType) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {sCol, obj, jdbcType}); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - setObject(sCol, obj, jdbcType.getVendorTypeNumber()); - - loggerExternal.exiting(getClassNameLogging(), "setObject"); - } - - public void setObject(String sCol, - Object obj, - SQLType jdbcType, - int scale) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {sCol, obj, jdbcType, scale}); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - setObject(sCol, obj, jdbcType.getVendorTypeNumber(), scale); - - loggerExternal.exiting(getClassNameLogging(), "setObject"); - } - - public void setObject(String sCol, - Object obj, - SQLType jdbcType, - int scale, - boolean forceEncrypt) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {sCol, obj, jdbcType, scale, forceEncrypt}); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - setObject(sCol, obj, jdbcType.getVendorTypeNumber(), scale, forceEncrypt); - - loggerExternal.exiting(getClassNameLogging(), "setObject"); - } - - public void registerOutParameter(String parameterName, - SQLType sqlType, - String typeName) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType, typeName}); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - registerOutParameter(parameterName, sqlType.getVendorTypeNumber(), typeName); - - loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); - } - - public void registerOutParameter(String parameterName, - SQLType sqlType, - int scale) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType, scale}); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - registerOutParameter(parameterName, sqlType.getVendorTypeNumber(), scale); - - loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); - } - - public void registerOutParameter(String parameterName, - SQLType sqlType, - int precision, - int scale) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType, scale}); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - registerOutParameter(parameterName, sqlType.getVendorTypeNumber(), precision, scale); - - loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); - } - - public void registerOutParameter(String parameterName, - SQLType sqlType) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType}); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - registerOutParameter(parameterName, sqlType.getVendorTypeNumber()); - - loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); - } - - public final void setObject(int index, - Object obj, - SQLType jdbcType) throws SQLServerException { - SQLServerPreparedStatement42Helper.setObject(this, index, obj, jdbcType); - } - - public final void setObject(int parameterIndex, - Object x, - SQLType targetSqlType, - int scaleOrLength) throws SQLServerException { - SQLServerPreparedStatement42Helper.setObject(this, parameterIndex, x, targetSqlType, scaleOrLength); - } - - public final void setObject(int parameterIndex, - Object x, - SQLType targetSqlType, - Integer precision, - Integer scale) throws SQLServerException { - SQLServerPreparedStatement42Helper.setObject(this, parameterIndex, x, targetSqlType, precision, scale); - } - - public final void setObject(int parameterIndex, - Object x, - SQLType targetSqlType, - Integer precision, - Integer scale, - boolean forceEncrypt) throws SQLServerException { - SQLServerPreparedStatement42Helper.setObject(this, parameterIndex, x, targetSqlType, precision, scale, forceEncrypt); - } - -} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerClob.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerClob.java index 137361dcb..31b8d0837 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerClob.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerClob.java @@ -17,10 +17,12 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; import java.io.Reader; import java.io.Serializable; import java.io.StringReader; import java.io.UnsupportedEncodingException; +import java.io.Writer; import java.sql.Clob; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; @@ -35,9 +37,13 @@ */ public class SQLServerClob extends SQLServerClobBase implements Clob { + /** + * Always refresh SerialVersionUID when prompted + */ private static final long serialVersionUID = 2872035282200133865L; - - // Loggers should be class static to avoid lock contention with multiple threads + + // Loggers should be class static to avoid lock contention with multiple + // threads private static final Logger logger = Logger.getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerClob"); /** @@ -67,12 +73,94 @@ public SQLServerClob(SQLServerConnection connection, super(null, stream, typeInfo.getSQLCollation(), logger, typeInfo); } + @Override + public void free() throws SQLException { + super.free(); + } + + @Override + public InputStream getAsciiStream() throws SQLException { + return super.getAsciiStream(); + } + + @Override + public Reader getCharacterStream() throws SQLException { + return super.getCharacterStream(); + } + + @Override + public Reader getCharacterStream(long pos, + long length) throws SQLException { + return super.getCharacterStream(pos, length); + } + + @Override + public String getSubString(long pos, + int length) throws SQLException { + return super.getSubString(pos, length); + } + + @Override + public long length() throws SQLException { + return super.length(); + } + + @Override + void fillFromStream() throws SQLException { + super.fillFromStream(); + } + + @Override + public long position(Clob searchstr, + long start) throws SQLException { + return super.position(searchstr, start); + } + + @Override + public long position(String searchstr, + long start) throws SQLException { + return super.position(searchstr, start); + } + + @Override + public void truncate(long len) throws SQLException { + super.truncate(len); + } + + @Override + public OutputStream setAsciiStream(long pos) throws SQLException { + return super.setAsciiStream(pos); + } + + @Override + public Writer setCharacterStream(long pos) throws SQLException { + return super.setCharacterStream(pos); + } + + @Override + public int setString(long pos, + String s) throws SQLException { + return super.setString(pos, s); + } + + @Override + public int setString(long pos, + String str, + int offset, + int len) throws SQLException { + return super.setString(pos, str, offset, len); + } + + @Override final JDBCType getJdbcType() { return JDBCType.CLOB; } } abstract class SQLServerClobBase extends SQLServerLob implements Serializable { + /** + * Always refresh SerialVersionUID when prompted + */ private static final long serialVersionUID = 8691072211054430124L; // The value of the CLOB that this Clob object represents. @@ -82,27 +170,31 @@ abstract class SQLServerClobBase extends SQLServerLob implements Serializable { private final SQLCollation sqlCollation; private boolean isClosed = false; - + private final TypeInfo typeInfo; // Active streams which must be closed when the Clob/NClob is closed // - // Initial size of the array is based on an assumption that a Clob/NClob object is - // typically used either for input or output, and then only once. The array size + // Initial size of the array is based on an assumption that a Clob/NClob + // object is + // typically used either for input or output, and then only once. The array + // size // grows automatically if multiple streams are used. private ArrayList activeStreams = new ArrayList<>(1); transient SQLServerConnection con; - + private final Logger logger; - + final private String traceID = getClass().getName().substring(1 + getClass().getName().lastIndexOf('.')) + ":" + nextInstanceID(); final public String toString() { return traceID; } - static private final AtomicInteger baseID = new AtomicInteger(0); // Unique id generator for each instance (used for logging). + // Unique id generator for each instance (used for logging). + static private final AtomicInteger baseID = new AtomicInteger(0); + // Returns unique id for each instance. private static int nextInstanceID() { @@ -162,7 +254,8 @@ private String getDisplayClassName() { */ public void free() throws SQLException { if (!isClosed) { - // Close active streams, ignoring any errors, since nothing can be done with them after that point anyway. + // Close active streams, ignoring any errors, since nothing can be + // done with them after that point anyway. if (null != activeStreams) { for (Closeable stream : activeStreams) { try { @@ -246,8 +339,8 @@ public Reader getCharacterStream() throws SQLException { */ public Reader getCharacterStream(long pos, long length) throws SQLException { - // Not implemented - throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + SQLServerException.throwFeatureNotSupportedException(); + return null; } /** @@ -286,12 +379,14 @@ public String getSubString(long pos, if (pos > value.length()) pos = value.length(); - // Bound the requested length to no larger than the remainder of the value beyond pos so that the + // Bound the requested length to no larger than the remainder of the + // value beyond pos so that the // endIndex computed for the substring call below is within bounds. if (length > value.length() - pos) length = (int) (value.length() - pos); - // Note String.substring uses beginIndex and endIndex (not pos and length), so calculate endIndex. + // Note String.substring uses beginIndex and endIndex (not pos and + // length), so calculate endIndex. return value.substring((int) pos, (int) pos + length); } @@ -306,23 +401,25 @@ public long length() throws SQLException { checkClosed(); if (value == null && activeStreams.get(0) instanceof PLPInputStream) { - return (long)((PLPInputStream)activeStreams.get(0)).payloadLength/2; + return (long) ((PLPInputStream) activeStreams.get(0)).payloadLength / 2; } return value.length(); } - + /** * Function for the result set to maintain clobs it has created + * * @throws SQLException */ void fillFromStream() throws SQLException { - if(!isClosed) { + if (!isClosed) { getStringFromStream(); } } - + /** * Converts the stream to String + * * @throws SQLServerException */ private void getStringFromStream() throws SQLServerException { @@ -389,7 +486,8 @@ public long position(String searchstr, SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } - // Back compat: Handle null search string as not found rather than throw an exception. + // Back compat: Handle null search string as not found rather than throw + // an exception. // JDBC spec doesn't describe the behavior for a null search string. if (null == searchstr) return -1; @@ -532,7 +630,8 @@ public int setString(long pos, } // Note position for Clob.setString is 1 based not zero based. - // Position must be in range of existing Clob data or exactly 1 character + // Position must be in range of existing Clob data or exactly 1 + // character // past the end of data to request "append" mode. if (pos < 1 || pos > value.length() + 1) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPositionIndex")); @@ -545,7 +644,8 @@ public int setString(long pos, // Overwrite past end of value case. if (len >= value.length() - pos) { - // Make sure the new value length wouldn't exceed the maximum allowed + // Make sure the new value length wouldn't exceed the maximum + // allowed DataTypes.getCheckedLength(con, getJdbcType(), pos + len, false); assert pos + len <= Integer.MAX_VALUE; @@ -581,11 +681,14 @@ public int setString(long pos, } } -// SQLServerClobWriter is a simple java.io.Writer interface implementing class that -// forwards all calls to SQLServerClob.setString. This class is returned to caller by +// SQLServerClobWriter is a simple java.io.Writer interface implementing class +// that +// forwards all calls to SQLServerClob.setString. This class is returned to +// caller by // SQLServerClob class when setCharacterStream is called. // -// SQLServerClobWriter starts writing at postion streamPos and continues to write +// SQLServerClobWriter starts writing at postion streamPos and continues to +// write // in a forward only manner. There is no reset with java.io.Writer. // final class SQLServerClobWriter extends java.io.Writer { @@ -655,11 +758,14 @@ private void checkClosed() throws IOException { } } -// SQLServerClobAsciiOutputStream is a simple java.io.OutputStream interface implementing class that -// forwards all calls to SQLServerClob.setString. This class is returned to caller by +// SQLServerClobAsciiOutputStream is a simple java.io.OutputStream interface +// implementing class that +// forwards all calls to SQLServerClob.setString. This class is returned to +// caller by // SQLServerClob class when setAsciiStream is called. // -// SQLServerClobAsciiOutputStream starts writing at character postion streamPos and continues to write +// SQLServerClobAsciiOutputStream starts writing at character postion streamPos +// and continues to write // in a forward only manner. Reset/mark are not supported. // final class SQLServerClobAsciiOutputStream extends java.io.OutputStream { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index c632bb9be..0c4b2bef9 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -11,30 +11,23 @@ import static java.nio.charset.StandardCharsets.UTF_16LE; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.net.DatagramPacket; import java.net.DatagramSocket; -import java.net.IDN; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; -import java.sql.Blob; import java.sql.CallableStatement; -import java.sql.Clob; import java.sql.Connection; import java.sql.DatabaseMetaData; -import java.sql.NClob; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLClientInfoException; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLPermission; import java.sql.SQLWarning; import java.sql.SQLXML; import java.sql.Savepoint; import java.sql.Statement; -import java.sql.Struct; import java.text.MessageFormat; import java.util.Arrays; import java.util.Enumeration; @@ -54,8 +47,6 @@ import javax.sql.XAConnection; import org.ietf.jgss.GSSCredential; -import org.ietf.jgss.GSSException; - import mssql.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; import mssql.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap.Builder; import mssql.googlecode.concurrentlinkedhashmap.EvictionListener; @@ -82,23 +73,33 @@ * details. */ -// Note all the public functions in this class also need to be defined in SQLServerConnectionPoolProxy. -public class SQLServerConnection implements ISQLServerConnection { +// NOTE: All the public functions in this class also need to be defined in SQLServerConnectionPoolProxy +// Declare all new custom (non-static) Public APIs in ISQLServerConnection interface such that they can also be implemented by +// SQLServerConnectionPoolProxy +public class SQLServerConnection implements ISQLServerConnection, java.io.Serializable { - long timerExpire; + /** + * Always refresh SerialVersionUID when prompted + */ + private static final long serialVersionUID = 1965647556064751510L; + + long timerExpire; boolean attemptRefreshTokenLocked = false; - // Threasholds related to when prepared statement handles are cleaned-up. 1 == immediately. + // Thresholds related to when prepared statement handles are cleaned-up. 1 == immediately. /** - * The default for the prepared statement clean-up action threshold (i.e. when sp_unprepare is called). - */ + * The default for the prepared statement clean-up action threshold (i.e. when sp_unprepare is called). + */ static final int DEFAULT_SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD = 10; // Used to set the initial default, can be changed later. private int serverPreparedStatementDiscardThreshold = -1; // Current limit for this particular connection. /** - * The default for if prepared statements should execute sp_executesql before following the prepare, unprepare pattern. - */ - static final boolean DEFAULT_ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT_CALL = false; // Used to set the initial default, can be changed later. false == use sp_executesql -> sp_prepexec -> sp_execute -> batched -> sp_unprepare pattern, true == skip sp_executesql part of pattern. + * The default for if prepared statements should execute sp_executesql before following the prepare, unprepare pattern. + */ + static final boolean DEFAULT_ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT_CALL = false; // Used to set the initial default, can be changed later. + // false == use sp_executesql -> sp_prepexec -> sp_execute + // -> batched -> sp_unprepare pattern, true == skip + // sp_executesql part of pattern. private Boolean enablePrepareOnFirstPreparedStatementCall = null; // Current limit for this particular connection. // Handle the actual queue of discarded prepared statements. @@ -107,7 +108,6 @@ public class SQLServerConnection implements ISQLServerConnection { private boolean fedAuthRequiredByUser = false; private boolean fedAuthRequiredPreLoginResponse = false; - private boolean federatedAuthenticationAcknowledged = false; private boolean federatedAuthenticationRequested = false; private boolean federatedAuthenticationInfoRequested = false; // Keep this distinct from _federatedAuthenticationRequested, since some fedauth // library types may not need more info @@ -117,12 +117,17 @@ public class SQLServerConnection implements ISQLServerConnection { private byte[] accessTokenInByte = null; private SqlFedAuthToken fedAuthToken = null; - + private String originalHostNameInCertificate = null; private Boolean isAzureDW = null; - static class Sha1HashKey { + static class Sha1HashKey implements java.io.Serializable { + + /** + * Always refresh SerialVersionUID when prompted + */ + private static final long serialVersionUID = 166788428640603097L; private byte[] bytes; Sha1HashKey(String sql, @@ -138,7 +143,7 @@ public boolean equals(Object obj) { if (!(obj instanceof Sha1HashKey)) return false; - return java.util.Arrays.equals(bytes, ((Sha1HashKey)obj).bytes); + return java.util.Arrays.equals(bytes, ((Sha1HashKey) obj).bytes); } public int hashCode() { @@ -159,16 +164,19 @@ private java.security.MessageDigest getSha1Digest() { /** * Used to keep track of an individual prepared statement handle. */ - class PreparedStatementHandle { + class PreparedStatementHandle { private int handle = 0; private final AtomicInteger handleRefCount = new AtomicInteger(); private boolean isDirectSql; - private volatile boolean evictedFromCache; - private volatile boolean explicitlyDiscarded; + private volatile boolean evictedFromCache; + private volatile boolean explicitlyDiscarded; private Sha1HashKey key; - PreparedStatementHandle(Sha1HashKey key, int handle, boolean isDirectSql, boolean isEvictedFromCache) { - this.key = key; + PreparedStatementHandle(Sha1HashKey key, + int handle, + boolean isDirectSql, + boolean isEvictedFromCache) { + this.key = key; this.handle = handle; this.isDirectSql = isDirectSql; this.setIsEvictedFromCache(isEvictedFromCache); @@ -187,9 +195,9 @@ private void setIsEvictedFromCache(boolean isEvictedFromCache) { /** Specify that this statement has been explicitly discarded from being used by the cache. */ void setIsExplicitlyDiscarded() { - this.explicitlyDiscarded = true; - - evictCachedPreparedStatementHandle(this); + this.explicitlyDiscarded = true; + + evictCachedPreparedStatementHandle(this); } /** Has the statement been explicitly discarded. */ @@ -211,12 +219,11 @@ boolean isDirectSql() { return isDirectSql; } - /** Make sure handle cannot be re-used. + /** + * Make sure handle cannot be re-used. * - * @return - * false: Handle could not be discarded, it is in use. - * true: Handle was successfully put on path for discarding. - */ + * @return false: Handle could not be discarded, it is in use. true: Handle was successfully put on path for discarding. + */ private boolean tryDiscardHandle() { return handleRefCount.compareAndSet(0, -999); } @@ -226,12 +233,12 @@ private boolean isDiscarded() { return 0 > handleRefCount.intValue(); } - /** Adds a new reference to this handle, i.e. re-using it. + /** + * Adds a new reference to this handle, i.e. re-using it. * - * @return - * false: Reference could not be added, statement has been discarded or does not have a handle associated with it. - * true: Reference was successfully added. - */ + * @return false: Reference could not be added, statement has been discarded or does not have a handle associated with it. true: Reference was + * successfully added. + */ boolean tryAddReference() { if (isDiscarded() || isExplicitlyDiscarded()) return false; @@ -241,7 +248,7 @@ boolean tryAddReference() { } } - /** Remove a reference from this handle*/ + /** Remove a reference from this handle */ void removeReference() { handleRefCount.decrementAndGet(); } @@ -254,34 +261,33 @@ void removeReference() { static private ConcurrentLinkedHashMap parsedSQLCache; static { - parsedSQLCache = new Builder() - .maximumWeightedCapacity(PARSED_SQL_CACHE_SIZE) - .build(); + parsedSQLCache = new Builder().maximumWeightedCapacity(PARSED_SQL_CACHE_SIZE).build(); } /** Get prepared statement cache entry if exists, if not parse and create a new one */ - static ParsedSQLCacheItem getCachedParsedSQL(Sha1HashKey key) { + static ParsedSQLCacheItem getCachedParsedSQL(Sha1HashKey key) { return parsedSQLCache.get(key); } /** Parse and create a information about parsed SQL text */ - static ParsedSQLCacheItem parseAndCacheSQL(Sha1HashKey key, String sql) throws SQLServerException { + static ParsedSQLCacheItem parseAndCacheSQL(Sha1HashKey key, + String sql) throws SQLServerException { JDBCSyntaxTranslator translator = new JDBCSyntaxTranslator(); String parsedSql = translator.translate(sql); - String procName = translator.getProcedureName(); // may return null + String procName = translator.getProcedureName(); // may return null boolean returnValueSyntax = translator.hasReturnValueSyntax(); int paramCount = countParams(parsedSql); - ParsedSQLCacheItem cacheItem = new ParsedSQLCacheItem (parsedSql, paramCount, procName, returnValueSyntax); + ParsedSQLCacheItem cacheItem = new ParsedSQLCacheItem(parsedSql, paramCount, procName, returnValueSyntax); parsedSQLCache.putIfAbsent(key, cacheItem); return cacheItem; } - + /** Default size for prepared statement caches */ - static final int DEFAULT_STATEMENT_POOLING_CACHE_SIZE = 0; - - /** Size of the prepared statement handle cache */ + static final int DEFAULT_STATEMENT_POOLING_CACHE_SIZE = 0; + + /** Size of the prepared statement handle cache */ private int statementPoolingCacheSize = DEFAULT_STATEMENT_POOLING_CACHE_SIZE; /** Cache of prepared statement handles */ @@ -293,23 +299,23 @@ static ParsedSQLCacheItem parseAndCacheSQL(Sha1HashKey key, String sql) throws */ private boolean disableStatementPooling = true; - /** - * Find statement parameters. - * - * @param sql - * SQL text to parse for number of parameters to intialize. - */ - private static int countParams(String sql) { - int nParams = 0; - - // Figure out the expected number of parameters by counting the - // parameter placeholders in the SQL string. - int offset = -1; - while ((offset = ParameterUtils.scanSQLForChar('?', sql, ++offset)) < sql.length()) - ++nParams; - - return nParams; - } + /** + * Find statement parameters. + * + * @param sql + * SQL text to parse for number of parameters to intialize. + */ + private static int countParams(String sql) { + int nParams = 0; + + // Figure out the expected number of parameters by counting the + // parameter placeholders in the SQL string. + int offset = -1; + while ((offset = ParameterUtils.scanSQLForChar('?', sql, ++offset)) < sql.length()) + ++nParams; + + return nParams; + } SqlFedAuthToken getAuthenticationResult() { return fedAuthToken; @@ -364,8 +370,6 @@ public String toString() { } } - - class ActiveDirectoryAuthentication { static final String JDBC_FEDAUTH_CLIENT_ID = "7f98cb04-cd1e-40df-9140-3bf7e2cea4db"; static final String ADAL_GET_ACCESS_TOKEN_FUNCTION_NAME = "ADALGetAccessToken"; @@ -393,7 +397,6 @@ private enum State { * Connection state variables. NB If new state is added then logical connections derived from a physical connection must inherit the same state. * If state variables are added they must be added also in connection cloning method clone() */ - private final static int INTERMITTENT_TLS_MAX_RETRY = 5; // Indicates if we received a routing ENVCHANGE in the current connection attempt @@ -408,24 +411,19 @@ ServerPortPlaceHolder getRoutingInfo() { // Permission targets private static final String callAbortPerm = "callAbort"; - + private static final String SET_NETWORK_TIMEOUT_PERM = "setNetworkTimeout"; - private boolean sendStringParametersAsUnicode = SQLServerDriverBooleanProperty.SEND_STRING_PARAMETERS_AS_UNICODE.getDefaultValue(); // see - // connection - // properties - // doc - // (default - // is - // false). + // see connection properties doc (default is false). + private boolean sendStringParametersAsUnicode = SQLServerDriverBooleanProperty.SEND_STRING_PARAMETERS_AS_UNICODE.getDefaultValue(); private String hostName = null; - + boolean sendStringParametersAsUnicode() { return sendStringParametersAsUnicode; } - private boolean lastUpdateCount; // see connection properties doc + private boolean lastUpdateCount; // see connection properties doc final boolean useLastUpdateCount() { return lastUpdateCount; @@ -457,8 +455,8 @@ final ApplicationIntent getApplicationIntent() { return applicationIntent; } - private int nLockTimeout; // see connection properties doc - private String selectMethod; // see connection properties doc 4.0 new property + private int nLockTimeout; // see connection properties doc + private String selectMethod; // see connection properties doc 4.0 new property final String getSelectMethod() { return selectMethod; @@ -475,13 +473,15 @@ final String getResponseBuffering() { final int getQueryTimeoutSeconds() { return queryTimeoutSeconds; } + /** * timeout value for canceling the query timeout */ private int cancelQueryTimeoutSeconds; - + /** * Retrieves the cancelTimeout in seconds + * * @return */ final int getCancelQueryTimeoutSeconds() { @@ -519,11 +519,7 @@ public void setUseBulkCopyForBatchInsert(boolean useBulkCopyForBatchInsert) { private boolean sendTimeAsDatetime = SQLServerDriverBooleanProperty.SEND_TIME_AS_DATETIME.getDefaultValue(); - /** - * Checks the sendTimeAsDatetime property. - * - * @return boolean value of sendTimeAsDatetime - */ + @Override public synchronized final boolean getSendTimeAsDatetime() { return !isKatmaiOrLater() || sendTimeAsDatetime; } @@ -932,12 +928,7 @@ int getServerMajorVersion() { private UUID clientConnectionId = null; - /** - * Retrieves the clientConnectionID. - * - * @throws SQLServerException - * when an error occurs - */ + @Override public UUID getClientConnectionId() throws SQLServerException { // If the connection is closed, we do not allow external application to get // ClientConnectionId. @@ -964,15 +955,6 @@ final boolean attachConnId() { connectionlogger.fine(toString() + " created by (" + parentInfo + ")"); initResettableValues(); - // JDBC 3 driver only works with 1.5 JRE - if (3 == DriverJDBCVersion.major && !"1.5".equals(Util.SYSTEM_SPEC_VERSION)) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unsupportedJREVersion")); - Object[] msgArgs = {Util.SYSTEM_SPEC_VERSION}; - String message = form.format(msgArgs); - connectionlogger.severe(message); - throw new UnsupportedOperationException(message); - } - // Caching turned on? if (!this.getDisableStatementPooling() && 0 < this.getStatementPoolingCacheSize()) { prepareCache(); @@ -1010,7 +992,7 @@ final void resetPooledConnection() { * * @return the next conn id */ - /* L0 */ private static int nextConnectionID() { + private static int nextConnectionID() { return baseConnectionID.incrementAndGet(); // 4.04 Ensure thread safe id allocation } @@ -1025,6 +1007,7 @@ String getClassNameLogging() { /** * This is a helper function to provide an ID string suitable for tracing. */ + @Override public String toString() { if (null != clientConnectionId) return traceID + " ClientConnectionId: " + clientConnectionId.toString(); @@ -1032,21 +1015,12 @@ public String toString() { return traceID; } - /** - * Throw a not implemeneted exception. - * - * @throws SQLServerException - */ - /* L0 */ void NotImplemented() throws SQLServerException { - SQLServerException.makeFromDriverError(this, this, SQLServerException.getErrString("R_notSupported"), null, false); - } - /** * Check if the connection is closed Create a new connection if it's a fedauth connection and the access token is going to expire. * * @throws SQLServerException */ - /* L0 */ void checkClosed() throws SQLServerException { + void checkClosed() throws SQLServerException { if (isSessionUnAvailable()) { SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_connectionIsClosed"), null, false); } @@ -1071,7 +1045,7 @@ public String toString() { * @exception SQLServerException * thrown if value is not recognized. */ - /* L0 */ private boolean booleanPropertyOn(String propName, + private boolean booleanPropertyOn(String propName, String propValue) throws SQLServerException { // Null means take the default of false. if (null == propValue) @@ -1166,8 +1140,8 @@ else if (elapsedSeconds >= loginTimeoutSeconds) { else { // Retry the connection. if (connectionlogger.isLoggable(Level.FINE)) { - connectionlogger - .fine("Connection failed during SSL handshake. Retrying due to an intermittent TLS 1.2 failure issue. Retry attempt = " + connectionlogger.fine( + "Connection failed during SSL handshake. Retrying due to an intermittent TLS 1.2 failure issue. Retry attempt = " + retryAttempt + "."); } } @@ -1231,20 +1205,19 @@ Connection connectInternal(Properties propsIn, pooledConnectionParent = pooledConnection; - String hostNameInCertificate = activeConnectionProperties. - getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString()); - + String hostNameInCertificate = activeConnectionProperties.getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString()); + // hostNameInCertificate property can change when redirection is involved, so maintain this value // for every instance of SQLServerConnection. if (null == originalHostNameInCertificate && null != hostNameInCertificate && !hostNameInCertificate.isEmpty()) { - originalHostNameInCertificate = activeConnectionProperties. - getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString()); + originalHostNameInCertificate = activeConnectionProperties + .getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString()); } - + if (null != originalHostNameInCertificate && !originalHostNameInCertificate.isEmpty()) { // if hostNameInCertificate has a legitimate value (and not empty or null), // reset hostNameInCertificate to the original value every time we connect (or re-connect). - activeConnectionProperties.setProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString(), + activeConnectionProperties.setProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString(), originalHostNameInCertificate); } @@ -1327,7 +1300,7 @@ Connection connectInternal(Properties propsIn, if (true == serverNameAsACE) { try { - sPropValue = IDN.toASCII(sPropValue); + sPropValue = java.net.IDN.toASCII(sPropValue); } catch (IllegalArgumentException ex) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_InvalidConnectionSetting")); @@ -1432,7 +1405,8 @@ Connection connectInternal(Properties propsIn, trustServerCertificate = booleanPropertyOn(sPropKey, sPropValue); trustManagerClass = activeConnectionProperties.getProperty(SQLServerDriverStringProperty.TRUST_MANAGER_CLASS.toString()); - trustManagerConstructorArg = activeConnectionProperties.getProperty(SQLServerDriverStringProperty.TRUST_MANAGER_CONSTRUCTOR_ARG.toString()); + trustManagerConstructorArg = activeConnectionProperties + .getProperty(SQLServerDriverStringProperty.TRUST_MANAGER_CONSTRUCTOR_ARG.toString()); sPropKey = SQLServerDriverStringProperty.SELECT_METHOD.toString(); sPropValue = activeConnectionProperties.getProperty(sPropKey); @@ -1495,7 +1469,7 @@ Connection connectInternal(Properties propsIn, sPropValue = activeConnectionProperties.getProperty(sPropKey); if (null != sPropValue) { setDisableStatementPooling(booleanPropertyOn(sPropKey, sPropValue)); - } + } sPropKey = SQLServerDriverBooleanProperty.INTEGRATED_SECURITY.toString(); sPropValue = activeConnectionProperties.getProperty(sPropKey); @@ -1512,14 +1486,14 @@ Connection connectInternal(Properties propsIn, } } - if(intAuthScheme == AuthenticationScheme.javaKerberos){ + if (intAuthScheme == AuthenticationScheme.javaKerberos) { sPropKey = SQLServerDriverObjectProperty.GSS_CREDENTIAL.toString(); - if(activeConnectionProperties.containsKey(sPropKey)) { + if (activeConnectionProperties.containsKey(sPropKey)) { ImpersonatedUserCred = (GSSCredential) activeConnectionProperties.get(sPropKey); isUserCreatedCredential = true; } } - + sPropKey = SQLServerDriverStringProperty.AUTHENTICATION.toString(); sPropValue = activeConnectionProperties.getProperty(sPropKey); if (sPropValue == null) { @@ -1751,18 +1725,18 @@ else if (0 == requestedPacketSize) SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false); } } - + sPropKey = SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.toString(); int cancelQueryTimeout = SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.getDefaultValue(); - + if (activeConnectionProperties.getProperty(sPropKey) != null && activeConnectionProperties.getProperty(sPropKey).length() > 0) { try { int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey)); if (n >= cancelQueryTimeout) { - // use cancelQueryTimeout only if queryTimeout is set. - if(queryTimeoutSeconds > defaultQueryTimeout) { - cancelQueryTimeoutSeconds = n; - } + // use cancelQueryTimeout only if queryTimeout is set. + if (queryTimeoutSeconds > defaultQueryTimeout) { + cancelQueryTimeoutSeconds = n; + } } else { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidCancelQueryTimeout")); @@ -1776,7 +1750,7 @@ else if (0 == requestedPacketSize) SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false); } } - + sPropKey = SQLServerDriverIntProperty.SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD.toString(); if (activeConnectionProperties.getProperty(sPropKey) != null && activeConnectionProperties.getProperty(sPropKey).length() > 0) { try { @@ -1811,7 +1785,7 @@ else if (0 == requestedPacketSize) else { activeConnectionProperties.setProperty(sPropKey, SSLProtocol.valueOfString(sPropValue).toString()); } - + FailoverInfo fo = null; String databaseNameProperty = SQLServerDriverStringProperty.DATABASE_NAME.toString(); String serverNameProperty = SQLServerDriverStringProperty.SERVER_NAME.toString(); @@ -2322,14 +2296,14 @@ private void connectHelper(ServerPortPlaceHolder serverInfo, connectionlogger.fine(toString() + " Connecting with server: " + serverInfo.getServerName() + " port: " + serverInfo.getPortNumber() + " Timeout slice: " + timeOutsliceInMillis + " Timeout Full: " + timeOutFullInSeconds); } - + // Before opening the TDSChannel, calculate local hostname // as the InetAddress.getLocalHost() takes more than usual time in certain OS and JVM combination, it avoids connection loss hostName = activeConnectionProperties.getProperty(SQLServerDriverStringProperty.WORKSTATION_ID.toString()); if (StringUtils.isEmpty(hostName)) { hostName = Util.lookupHostName(); } - + // if the timeout is infinite slices are infinite too. tdsChannel = new TDSChannel(this); if (0 == timeOutFullInSeconds) @@ -2858,7 +2832,7 @@ final boolean doExecute() throws SQLServerException { * * @return the syntax string */ - /* L0 */ private String sqlStatementToInitialize() { + private String sqlStatementToInitialize() { String s = null; if (nLockTimeout > -1) s = " set lock_timeout " + nLockTimeout; @@ -2872,7 +2846,7 @@ final boolean doExecute() throws SQLServerException { * the new catalog * @return the required syntax */ - /* L0 */ void setCatalogName(String sDB) { + void setCatalogName(String sDB) { if (sDB != null) { if (sDB.length() > 0) { sCatalog = sDB; @@ -2885,7 +2859,7 @@ final boolean doExecute() throws SQLServerException { * * @return the required syntax */ - /* L0 */ String sqlStatementToSetTransactionIsolationLevel() throws SQLServerException { + String sqlStatementToSetTransactionIsolationLevel() throws SQLServerException { String sql = "set transaction isolation level "; switch (transactionIsolationLevel) { @@ -2927,34 +2901,39 @@ static String sqlStatementToSetCommit(boolean autoCommit) { return (true == autoCommit) ? "set implicit_transactions off " : "set implicit_transactions on "; } - /* L0 */ public Statement createStatement() throws SQLServerException { + @Override + public Statement createStatement() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "createStatement"); Statement st = createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); loggerExternal.exiting(getClassNameLogging(), "createStatement", st); return st; } - /* L0 */ public PreparedStatement prepareStatement(String sql) throws SQLServerException { + @Override + public PreparedStatement prepareStatement(String sql) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "prepareStatement", sql); PreparedStatement pst = prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); loggerExternal.exiting(getClassNameLogging(), "prepareStatement", pst); return pst; } - /* L0 */ public CallableStatement prepareCall(String sql) throws SQLServerException { + @Override + public CallableStatement prepareCall(String sql) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "prepareCall", sql); CallableStatement st = prepareCall(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); loggerExternal.exiting(getClassNameLogging(), "prepareCall", st); return st; } - /* L0 */ public String nativeSQL(String sql) throws SQLServerException { + @Override + public String nativeSQL(String sql) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "nativeSQL", sql); checkClosed(); loggerExternal.exiting(getClassNameLogging(), "nativeSQL", sql); return sql; } + @Override public void setAutoCommit(boolean newAutoCommitMode) throws SQLServerException { if (loggerExternal.isLoggable(Level.FINER)) { loggerExternal.entering(getClassNameLogging(), "setAutoCommit", newAutoCommitMode); @@ -2982,7 +2961,8 @@ public void setAutoCommit(boolean newAutoCommitMode) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "setAutoCommit"); } - /* L0 */ public boolean getAutoCommit() throws SQLServerException { + @Override + public boolean getAutoCommit() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getAutoCommit"); checkClosed(); boolean res = !inXATransaction && databaseAutoCommitMode; @@ -2991,16 +2971,11 @@ public void setAutoCommit(boolean newAutoCommitMode) throws SQLServerException { return res; } - /* LO */ final byte[] getTransactionDescriptor() { + final byte[] getTransactionDescriptor() { return transactionDescriptor; } - /** - * Commit a transcation. Per our transaction spec, see also SDT#410729, a commit in autocommit mode = true is a NO-OP. - * - * @throws SQLServerException - * if no transaction exists. - */ + @Override public void commit() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "commit"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -3013,12 +2988,7 @@ public void commit() throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "commit"); } - /** - * Rollback a transcation. - * - * @throws SQLServerException - * if no transaction exists or if the connection is in auto-commit mode. - */ + @Override public void rollback() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "rollback"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -3034,6 +3004,7 @@ public void rollback() throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "rollback"); } + @Override public void abort(Executor executor) throws SQLException { loggerExternal.entering(getClassNameLogging(), "abort", executor); @@ -3063,17 +3034,13 @@ public void abort(Executor executor) throws SQLException { setState(State.Closed); - executor.execute(new Runnable() { - public void run() { - if (null != tdsChannel) { - tdsChannel.close(); - } - } - }); + if (null != tdsChannel) + executor.execute(() -> tdsChannel.close()); loggerExternal.exiting(getClassNameLogging(), "abort"); } + @Override public void close() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "close"); @@ -3098,7 +3065,7 @@ public void close() throws SQLServerException { // Clean-up queue etc. related to batching of prepared statement discard actions (sp_unprepare). cleanupPreparedStatementDiscardActions(); - + ActivityCorrelator.cleanupActivityId(); loggerExternal.exiting(getClassNameLogging(), "close"); @@ -3128,13 +3095,15 @@ final void poolCloseEventNotify() throws SQLServerException { } - /* L0 */ public boolean isClosed() throws SQLServerException { + @Override + public boolean isClosed() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "isClosed"); loggerExternal.exiting(getClassNameLogging(), "isClosed", isSessionUnAvailable()); return isSessionUnAvailable(); } - /* L0 */ public DatabaseMetaData getMetaData() throws SQLServerException { + @Override + public DatabaseMetaData getMetaData() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getMetaData"); checkClosed(); if (databaseMetaData == null) { @@ -3144,7 +3113,8 @@ final void poolCloseEventNotify() throws SQLServerException { return databaseMetaData; } - /* L0 */ public void setReadOnly(boolean readOnly) throws SQLServerException { + @Override + public void setReadOnly(boolean readOnly) throws SQLServerException { if (loggerExternal.isLoggable(Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setReadOnly", readOnly); checkClosed(); @@ -3152,7 +3122,8 @@ final void poolCloseEventNotify() throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "setReadOnly"); } - /* L0 */ public boolean isReadOnly() throws SQLServerException { + @Override + public boolean isReadOnly() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "isReadOnly"); checkClosed(); if (loggerExternal.isLoggable(Level.FINER)) @@ -3160,7 +3131,8 @@ final void poolCloseEventNotify() throws SQLServerException { return false; } - /* L0 */ public void setCatalog(String catalog) throws SQLServerException { + @Override + public void setCatalog(String catalog) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "setCatalog", catalog); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); @@ -3173,7 +3145,8 @@ final void poolCloseEventNotify() throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "setCatalog"); } - /* L0 */ public String getCatalog() throws SQLServerException { + @Override + public String getCatalog() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getCatalog"); checkClosed(); loggerExternal.exiting(getClassNameLogging(), "getCatalog", sCatalog); @@ -3184,7 +3157,8 @@ String getSCatalog() throws SQLServerException { return sCatalog; } - /* L0 */ public void setTransactionIsolation(int level) throws SQLServerException { + @Override + public void setTransactionIsolation(int level) throws SQLServerException { if (loggerExternal.isLoggable(Level.FINER)) { loggerExternal.entering(getClassNameLogging(), "setTransactionIsolation", level); if (Util.IsActivityTraceOn()) { @@ -3202,7 +3176,8 @@ String getSCatalog() throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "setTransactionIsolation"); } - /* L0 */ public int getTransactionIsolation() throws SQLServerException { + @Override + public int getTransactionIsolation() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getTransactionIsolation"); checkClosed(); if (loggerExternal.isLoggable(Level.FINER)) @@ -3214,7 +3189,8 @@ String getSCatalog() throws SQLServerException { Object warningSynchronization = new Object(); // Think about returning a copy when we implement additional warnings. - /* L0 */ public SQLWarning getWarnings() throws SQLServerException { + @Override + public SQLWarning getWarnings() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getWarnings"); checkClosed(); // check null warn wont crash @@ -3236,7 +3212,8 @@ private void addWarning(String warningString) { } } - /* L2 */ public void clearWarnings() throws SQLServerException { + @Override + public void clearWarnings() throws SQLServerException { synchronized (warningSynchronization) { loggerExternal.entering(getClassNameLogging(), "clearWarnings"); checkClosed(); @@ -3246,11 +3223,11 @@ private void addWarning(String warningString) { } // --------------------------JDBC 2.0----------------------------- + @Override public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLServerException { if (loggerExternal.isLoggable(Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "createStatement", - new Object[] {resultSetType, resultSetConcurrency}); + loggerExternal.entering(getClassNameLogging(), "createStatement", new Object[] {resultSetType, resultSetConcurrency}); checkClosed(); Statement st = new SQLServerStatement(this, resultSetType, resultSetConcurrency, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); @@ -3261,25 +3238,17 @@ public Statement createStatement(int resultSetType, return st; } + @Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLServerException { if (loggerExternal.isLoggable(Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "prepareStatement", - new Object[] {sql, resultSetType, resultSetConcurrency}); + loggerExternal.entering(getClassNameLogging(), "prepareStatement", new Object[] {sql, resultSetType, resultSetConcurrency}); checkClosed(); - PreparedStatement st; - - // Make sure SQLServerPreparedStatement42 is used for 4.2 and above. - if (Util.use42Wrapper() || Util.use43Wrapper()) { - st = new SQLServerPreparedStatement42(this, sql, resultSetType, resultSetConcurrency, - SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); - } - else { - st = new SQLServerPreparedStatement(this, sql, resultSetType, resultSetConcurrency, - SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); - } + PreparedStatement st = new SQLServerPreparedStatement(this, sql, resultSetType, resultSetConcurrency, + SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); + if (requestStarted) { addOpenStatement(st); } @@ -3296,49 +3265,37 @@ private PreparedStatement prepareStatement(String sql, new Object[] {sql, resultSetType, resultSetConcurrency, stmtColEncSetting}); checkClosed(); - PreparedStatement st; + PreparedStatement st = new SQLServerPreparedStatement(this, sql, resultSetType, resultSetConcurrency, stmtColEncSetting); - // Make sure SQLServerPreparedStatement42 is used for 4.2 and above. - if (Util.use42Wrapper() || Util.use43Wrapper()) { - st = new SQLServerPreparedStatement42(this, sql, resultSetType, resultSetConcurrency, stmtColEncSetting); - } - else { - st = new SQLServerPreparedStatement(this, sql, resultSetType, resultSetConcurrency, stmtColEncSetting); - } if (requestStarted) { addOpenStatement(st); } + loggerExternal.exiting(getClassNameLogging(), "prepareStatement", st); return st; } + @Override public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLServerException { if (loggerExternal.isLoggable(Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "prepareCall", - new Object[] {sql, resultSetType, resultSetConcurrency}); + loggerExternal.entering(getClassNameLogging(), "prepareCall", new Object[] {sql, resultSetType, resultSetConcurrency}); checkClosed(); - CallableStatement st; + CallableStatement st = new SQLServerCallableStatement(this, sql, resultSetType, resultSetConcurrency, + SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); - // Make sure SQLServerCallableStatement42 is used for 4.2 and above. - if (Util.use42Wrapper() || Util.use43Wrapper()) { - st = new SQLServerCallableStatement42(this, sql, resultSetType, resultSetConcurrency, - SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); - } - else { - st = new SQLServerCallableStatement(this, sql, resultSetType, resultSetConcurrency, - SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); - } if (requestStarted) { addOpenStatement(st); } + loggerExternal.exiting(getClassNameLogging(), "prepareCall", st); return st; } - /* L2 */ public void setTypeMap(java.util.Map> map) throws SQLServerException { + @Override + public void setTypeMap(java.util.Map> map) throws SQLException { loggerExternal.entering(getClassNameLogging(), "setTypeMap", map); checkClosed(); if (map != null && (map instanceof java.util.HashMap)) { @@ -3349,9 +3306,10 @@ public CallableStatement prepareCall(String sql, } } - NotImplemented(); + SQLServerException.throwNotSupportedException(this, null); } + @Override public java.util.Map> getTypeMap() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getTypeMap"); checkClosed(); @@ -3499,7 +3457,7 @@ final boolean doExecute() throws SQLServerException { } } - /* L0 */ private void logon(LogonCommand command) throws SQLServerException { + private void logon(LogonCommand command) throws SQLServerException { SSPIAuthentication authentication = null; if (integratedSecurity && AuthenticationScheme.nativeAuthentication == intAuthScheme) authentication = new AuthenticationJNI(this, currentConnectPlaceHolder.getServerName(), currentConnectPlaceHolder.getPortNumber()); @@ -3542,7 +3500,8 @@ final boolean doExecute() throws SQLServerException { connectionCommand(sqlStmt, "Change Settings"); } } - } finally { + } + finally { if (integratedSecurity) { if (null != authentication) { authentication.ReleaseClientContext(); @@ -3568,6 +3527,7 @@ final boolean doExecute() throws SQLServerException { private static final int ENVCHANGE_DTC_ENLIST = 11; private static final int ENVCHANGE_DTC_DEFECT = 12; private static final int ENVCHANGE_CHANGE_MIRROR = 13; + @SuppressWarnings("unused") private static final int ENVCHANGE_UNUSED_14 = 14; private static final int ENVCHANGE_DTC_PROMOTE = 15; private static final int ENVCHANGE_DTC_MGR_ADDR = 16; @@ -3602,7 +3562,7 @@ final void processEnvChange(TDSReader tdsReader) throws SQLServerException { try { databaseCollation = new SQLCollation(tdsReader); } - catch (UnsupportedEncodingException e) { + catch (java.io.UnsupportedEncodingException e) { terminate(SQLServerException.DRIVER_ERROR_INVALID_TDS, e.getMessage(), e); } @@ -3730,8 +3690,11 @@ final void processEnvChange(TDSReader tdsReader) throws SQLServerException { // Check if the hostNameInCertificate needs to be updated to handle the rerouted subdomain in Azure String currentHostName = activeConnectionProperties.getProperty("hostNameInCertificate"); - if (null != currentHostName && currentHostName.startsWith("*") - && (null != routingServerName) /* skip the check for hostNameInCertificate if routingServerName is null */ + if (null != currentHostName && currentHostName.startsWith("*") && (null != routingServerName) /* + * skip the check for + * hostNameInCertificate if + * routingServerName is null + */ && routingServerName.indexOf('.') != -1) { char[] currentHostNameCharArray = currentHostName.toCharArray(); char[] routingServerNameCharArray = routingServerName.toCharArray(); @@ -3967,7 +3930,7 @@ private SqlFedAuthToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throws SQLSe // No:of milliseconds to sleep for the inital back off. int sleepInterval = 100; - + while (true) { if (authenticationString.trim().equalsIgnoreCase(SqlAuthentication.ActiveDirectoryPassword.toString())) { fedAuthToken = SQLServerADAL4JUtils.getSqlFedAuthToken(fedAuthInfo, user, password, authenticationString); @@ -3976,7 +3939,7 @@ private SqlFedAuthToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throws SQLSe break; } else if (authenticationString.trim().equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString())) { - + // If operating system is windows and sqljdbc_auth is loaded then choose the DLL authentication. if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows") && AuthenticationJNI.isDllLoaded()) { try { @@ -4159,8 +4122,6 @@ private void onFeatureExtAck(byte featureId, Object[] msgArgs = {fedAuthFeatureExtensionData.libraryType}; throw new SQLServerException(form.format(msgArgs), null); } - federatedAuthenticationAcknowledged = true; - break; } case TDS.TDS_FEATURE_EXT_AE: { @@ -4308,7 +4269,7 @@ final void JTAEnlistConnection(byte cookie[]) throws SQLServerException { * @throws SQLServerException * @return the encoded data */ - /* L0 */ private byte[] toUCS16(String s) throws SQLServerException { + private byte[] toUCS16(String s) throws SQLServerException { if (s == null) return new byte[0]; int l = s.length(); @@ -4330,7 +4291,7 @@ final void JTAEnlistConnection(byte cookie[]) throws SQLServerException { * the password * @return the encryption */ - /* L0 */ private byte[] encryptPassword(String pwd) { + private byte[] encryptPassword(String pwd) { // Changed to handle non ascii passwords if (pwd == null) pwd = ""; @@ -4541,8 +4502,9 @@ else if (serverMajorVersion >= 9) // Yukon (9.0) --> TDS 7.2 // Prelogin disconn TDS.LOGIN_OPTION2_INTEGRATED_SECURITY_ON : TDS.LOGIN_OPTION2_INTEGRATED_SECURITY_OFF))); // TypeFlags - tdsWriter.writeByte((byte) (TDS.LOGIN_SQLTYPE_DEFAULT | (applicationIntent != null && applicationIntent.equals(ApplicationIntent.READ_ONLY) - ? TDS.LOGIN_READ_ONLY_INTENT : TDS.LOGIN_READ_WRITE_INTENT))); + tdsWriter.writeByte((byte) (TDS.LOGIN_SQLTYPE_DEFAULT + | (applicationIntent != null && applicationIntent.equals(ApplicationIntent.READ_ONLY) ? TDS.LOGIN_READ_ONLY_INTENT + : TDS.LOGIN_READ_WRITE_INTENT))); // OptionFlags3 byte colEncSetting; @@ -4736,22 +4698,22 @@ private void checkMatchesCurrentHoldability(int resultSetHoldability) throws SQL } } + @Override public Statement createStatement(int nType, int nConcur, int resultSetHoldability) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "createStatement", - new Object[] {nType, nConcur, resultSetHoldability}); + loggerExternal.entering(getClassNameLogging(), "createStatement", new Object[] {nType, nConcur, resultSetHoldability}); Statement st = createStatement(nType, nConcur, resultSetHoldability, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); loggerExternal.exiting(getClassNameLogging(), "createStatement", st); return st; } + @Override public Statement createStatement(int nType, int nConcur, int resultSetHoldability, SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "createStatement", - new Object[] {nType, nConcur, resultSetHoldability, stmtColEncSetting}); + loggerExternal.entering(getClassNameLogging(), "createStatement", new Object[] {nType, nConcur, resultSetHoldability, stmtColEncSetting}); checkClosed(); checkValidHoldability(resultSetHoldability); checkMatchesCurrentHoldability(resultSetHoldability); @@ -4763,112 +4725,75 @@ public Statement createStatement(int nType, return st; } - /* L3 */ public PreparedStatement prepareStatement(java.lang.String sql, + @Override + public PreparedStatement prepareStatement(java.lang.String sql, int nType, int nConcur, int resultSetHoldability) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "prepareStatement", - new Object[] {nType, nConcur, resultSetHoldability}); + loggerExternal.entering(getClassNameLogging(), "prepareStatement", new Object[] {nType, nConcur, resultSetHoldability}); PreparedStatement st = prepareStatement(sql, nType, nConcur, resultSetHoldability, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); loggerExternal.exiting(getClassNameLogging(), "prepareStatement", st); return st; } - /** - * Creates a PreparedStatement object that will generate ResultSet objects with the given type, concurrency, and - * holdability. - *

- * This method is the same as the prepareStatement method above, but it allows the default result set type, concurrency, and - * holdability to be overridden. - * - * @param sql - * a String object that is the SQL statement to be sent to the database; may contain one or more '?' IN parameters - * @param nType - * one of the following ResultSet constants: ResultSet.TYPE_FORWARD_ONLY, - * ResultSet.TYPE_SCROLL_INSENSITIVE, or ResultSet.TYPE_SCROLL_SENSITIVE - * @param nConcur - * one of the following ResultSet constants: ResultSet.CONCUR_READ_ONLY or - * ResultSet.CONCUR_UPDATABLE - * @param resultSetHoldability - * one of the following ResultSet constants: ResultSet.HOLD_CURSORS_OVER_COMMIT or - * ResultSet.CLOSE_CURSORS_AT_COMMIT - * @param stmtColEncSetting - * Specifies how data will be sent and received when reading and writing encrypted columns. - * @return a new PreparedStatement object, containing the pre-compiled SQL statement, that will generate ResultSet - * objects with the given type, concurrency, and holdability - * @throws SQLServerException - * if a database access error occurs, this method is called on a closed connection or the given parameters are not - * ResultSet constants indicating type, concurrency, and holdability - */ + @Override public PreparedStatement prepareStatement(java.lang.String sql, int nType, int nConcur, int resultSetHoldability, SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "prepareStatement", - new Object[] {nType, nConcur, resultSetHoldability, stmtColEncSetting}); + loggerExternal.entering(getClassNameLogging(), "prepareStatement", new Object[] {nType, nConcur, resultSetHoldability, stmtColEncSetting}); checkClosed(); checkValidHoldability(resultSetHoldability); checkMatchesCurrentHoldability(resultSetHoldability); - PreparedStatement st; + PreparedStatement st = new SQLServerPreparedStatement(this, sql, nType, nConcur, stmtColEncSetting); - // Make sure SQLServerPreparedStatement42 is used for 4.2 and above. - if (Util.use42Wrapper() || Util.use43Wrapper()) { - st = new SQLServerPreparedStatement42(this, sql, nType, nConcur, stmtColEncSetting); - } - else { - st = new SQLServerPreparedStatement(this, sql, nType, nConcur, stmtColEncSetting); - } if (requestStarted) { addOpenStatement(st); } + loggerExternal.exiting(getClassNameLogging(), "prepareStatement", st); return st; } - /* L3 */ public CallableStatement prepareCall(String sql, + @Override + public CallableStatement prepareCall(String sql, int nType, int nConcur, int resultSetHoldability) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "prepareStatement", - new Object[] {nType, nConcur, resultSetHoldability}); + loggerExternal.entering(getClassNameLogging(), "prepareStatement", new Object[] {nType, nConcur, resultSetHoldability}); CallableStatement st = prepareCall(sql, nType, nConcur, resultSetHoldability, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); loggerExternal.exiting(getClassNameLogging(), "prepareCall", st); return st; } + @Override public CallableStatement prepareCall(String sql, int nType, int nConcur, int resultSetHoldability, SQLServerStatementColumnEncryptionSetting stmtColEncSetiing) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "prepareStatement", - new Object[] {nType, nConcur, resultSetHoldability, stmtColEncSetiing}); + loggerExternal.entering(getClassNameLogging(), "prepareStatement", new Object[] {nType, nConcur, resultSetHoldability, stmtColEncSetiing}); checkClosed(); checkValidHoldability(resultSetHoldability); checkMatchesCurrentHoldability(resultSetHoldability); - CallableStatement st; + CallableStatement st = new SQLServerCallableStatement(this, sql, nType, nConcur, stmtColEncSetiing); - // Make sure SQLServerCallableStatement42 is used for 4.2 and above - if (Util.use42Wrapper() || Util.use43Wrapper()) { - st = new SQLServerCallableStatement42(this, sql, nType, nConcur, stmtColEncSetiing); - } - else { - st = new SQLServerCallableStatement(this, sql, nType, nConcur, stmtColEncSetiing); - } if (requestStarted) { addOpenStatement(st); } + loggerExternal.exiting(getClassNameLogging(), "prepareCall", st); return st; } /* JDBC 3.0 Auto generated keys */ - /* L3 */ public PreparedStatement prepareStatement(String sql, + @Override + public PreparedStatement prepareStatement(String sql, int flag) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "prepareStatement", new Object[] {sql, flag}); @@ -4879,33 +4804,7 @@ public CallableStatement prepareCall(String sql, return ps; } - /** - * Creates a default PreparedStatement object that has the capability to retrieve auto-generated keys. The given constant tells the - * driver whether it should make auto-generated keys available for retrieval. This parameter is ignored if the SQL statement is not an - * INSERT statement, or an SQL statement able to return auto-generated keys (the list of such statements is vendor-specific). - *

- * Note: This method is optimized for handling parametric SQL statements that benefit from precompilation. If the driver supports - * precompilation, the method prepareStatement will send the statement to the database for precompilation. Some drivers may not - * support precompilation. In this case, the statement may not be sent to the database until the PreparedStatement object is - * executed. This has no direct effect on users; however, it does affect which methods throw certain SQLExceptions. - *

- * Result sets created using the returned PreparedStatement object will by default be type TYPE_FORWARD_ONLY and have a - * concurrency level of CONCUR_READ_ONLY. The holdability of the created result sets can be determined by calling - * {@link #getHoldability}. - * - * @param sql - * an SQL statement that may contain one or more '?' IN parameter placeholders - * @param flag - * a flag indicating whether auto-generated keys should be returned; one of Statement.RETURN_GENERATED_KEYS or - * Statement.NO_GENERATED_KEYS - * @param stmtColEncSetting - * Specifies how data will be sent and received when reading and writing encrypted columns. - * @return a new PreparedStatement object, containing the pre-compiled SQL statement, that will have the capability of returning - * auto-generated keys - * @throws SQLServerException - * if a database access error occurs, this method is called on a closed connection or the given parameter is not a - * Statement constant indicating whether auto-generated keys should be returned - */ + @Override public PreparedStatement prepareStatement(String sql, int flag, SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { @@ -4918,7 +4817,8 @@ public PreparedStatement prepareStatement(String sql, return ps; } - /* L3 */ public PreparedStatement prepareStatement(String sql, + @Override + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "prepareStatement", new Object[] {sql, columnIndexes}); SQLServerPreparedStatement ps = (SQLServerPreparedStatement) prepareStatement(sql, columnIndexes, @@ -4928,35 +4828,7 @@ public PreparedStatement prepareStatement(String sql, return ps; } - /** - * Creates a default PreparedStatement object capable of returning the auto-generated keys designated by the given array. This array - * contains the indexes of the columns in the target table that contain the auto-generated keys that should be made available. The driver will - * ignore the array if the SQL statement is not an INSERT statement, or an SQL statement able to return auto-generated keys (the list - * of such statements is vendor-specific). - *

- * An SQL statement with or without IN parameters can be pre-compiled and stored in a PreparedStatement object. This object can then - * be used to efficiently execute this statement multiple times. - *

- * Note: This method is optimized for handling parametric SQL statements that benefit from precompilation. If the driver supports - * precompilation, the method prepareStatement will send the statement to the database for precompilation. Some drivers may not - * support precompilation. In this case, the statement may not be sent to the database until the PreparedStatement object is - * executed. This has no direct effect on users; however, it does affect which methods throw certain SQLExceptions. - *

- * Result sets created using the returned PreparedStatement object will by default be type TYPE_FORWARD_ONLY and have a - * concurrency level of CONCUR_READ_ONLY. The holdability of the created result sets can be determined by calling - * {@link #getHoldability}. - * - * @param sql - * an SQL statement that may contain one or more '?' IN parameter placeholders - * @param columnIndexes - * an array of column indexes indicating the columns that should be returned from the inserted row or rows - * @param stmtColEncSetting - * Specifies how data will be sent and received when reading and writing encrypted columns. - * @return a new PreparedStatement object, containing the pre-compiled statement, that is capable of returning the auto-generated - * keys designated by the given array of column indexes - * @throws SQLServerException - * if a database access error occurs or this method is called on a closed connection - */ + @Override public PreparedStatement prepareStatement(String sql, int[] columnIndexes, SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { @@ -4973,7 +4845,8 @@ public PreparedStatement prepareStatement(String sql, return ps; } - /* L3 */ public PreparedStatement prepareStatement(String sql, + @Override + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "prepareStatement", new Object[] {sql, columnNames}); @@ -4984,35 +4857,7 @@ public PreparedStatement prepareStatement(String sql, return ps; } - /** - * Creates a default PreparedStatement object capable of returning the auto-generated keys designated by the given array. This array - * contains the names of the columns in the target table that contain the auto-generated keys that should be returned. The driver will ignore the - * array if the SQL statement is not an INSERT statement, or an SQL statement able to return auto-generated keys (the list of such - * statements is vendor-specific). - *

- * An SQL statement with or without IN parameters can be pre-compiled and stored in a PreparedStatement object. This object can then - * be used to efficiently execute this statement multiple times. - *

- * Note: This method is optimized for handling parametric SQL statements that benefit from precompilation. If the driver supports - * precompilation, the method prepareStatement will send the statement to the database for precompilation. Some drivers may not - * support precompilation. In this case, the statement may not be sent to the database until the PreparedStatement object is - * executed. This has no direct effect on users; however, it does affect which methods throw certain SQLExceptions. - *

- * Result sets created using the returned PreparedStatement object will by default be type TYPE_FORWARD_ONLY and have a - * concurrency level of CONCUR_READ_ONLY. The holdability of the created result sets can be determined by calling - * {@link #getHoldability}. - * - * @param sql - * an SQL statement that may contain one or more '?' IN parameter placeholders - * @param columnNames - * an array of column names indicating the columns that should be returned from the inserted row or rows - * @param stmtColEncSetting - * Specifies how data will be sent and received when reading and writing encrypted columns. - * @return a new PreparedStatement object, containing the pre-compiled statement, that is capable of returning the auto-generated - * keys designated by the given array of column names - * @throws SQLServerException - * if a database access error occurs or this method is called on a closed connection - */ + @Override public PreparedStatement prepareStatement(String sql, String[] columnNames, SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { @@ -5030,9 +4875,10 @@ public PreparedStatement prepareStatement(String sql, /* JDBC 3.0 Savepoints */ - public void releaseSavepoint(Savepoint savepoint) throws SQLServerException { + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { loggerExternal.entering(getClassNameLogging(), "releaseSavepoint", savepoint); - NotImplemented(); + SQLServerException.throwNotSupportedException(this, null); } final private Savepoint setNamedSavepoint(String sName) throws SQLServerException { @@ -5055,7 +4901,8 @@ final private Savepoint setNamedSavepoint(String sName) throws SQLServerExceptio return s; } - /* L3 */ public Savepoint setSavepoint(String sName) throws SQLServerException { + @Override + public Savepoint setSavepoint(String sName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "setSavepoint", sName); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); @@ -5066,7 +4913,8 @@ final private Savepoint setNamedSavepoint(String sName) throws SQLServerExceptio return pt; } - /* L3 */ public Savepoint setSavepoint() throws SQLServerException { + @Override + public Savepoint setSavepoint() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "setSavepoint"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); @@ -5077,7 +4925,8 @@ final private Savepoint setNamedSavepoint(String sName) throws SQLServerExceptio return pt; } - /* L3 */ public void rollback(Savepoint s) throws SQLServerException { + @Override + public void rollback(Savepoint s) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "rollback", s); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); @@ -5090,13 +4939,15 @@ final private Savepoint setNamedSavepoint(String sName) throws SQLServerExceptio loggerExternal.exiting(getClassNameLogging(), "rollback"); } - /* L3 */ public int getHoldability() throws SQLServerException { + @Override + public int getHoldability() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getHoldability"); if (loggerExternal.isLoggable(Level.FINER)) loggerExternal.exiting(getClassNameLogging(), "getHoldability", holdability); return holdability; } + @Override public void setHoldability(int holdability) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "setHoldability", holdability); @@ -5119,6 +4970,7 @@ public void setHoldability(int holdability) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "setHoldability"); } + @Override public int getNetworkTimeout() throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNetworkTimeout"); @@ -5136,6 +4988,7 @@ public int getNetworkTimeout() throws SQLException { return timeout; } + @Override public void setNetworkTimeout(Executor executor, int timeout) throws SQLException { loggerExternal.entering(getClassNameLogging(), "setNetworkTimeout", timeout); @@ -5147,7 +5000,7 @@ public void setNetworkTimeout(Executor executor, } checkClosed(); - + // check for setNetworkTimeout permission SecurityManager secMgr = System.getSecurityManager(); if (secMgr != null) { @@ -5172,6 +5025,7 @@ public void setNetworkTimeout(Executor executor, loggerExternal.exiting(getClassNameLogging(), "setNetworkTimeout"); } + @Override public String getSchema() throws SQLException { loggerExternal.entering(getClassNameLogging(), "getSchema"); @@ -5211,6 +5065,7 @@ public String getSchema() throws SQLException { return null; } + @Override public void setSchema(String schema) throws SQLException { loggerExternal.entering(getClassNameLogging(), "setSchema", schema); checkClosed(); @@ -5219,61 +5074,58 @@ public void setSchema(String schema) throws SQLException { loggerExternal.exiting(getClassNameLogging(), "setSchema"); } - /** - * Modifies the setting of the sendTimeAsDatetime connection property. When true, java.sql.Time values will be sent to the server as SQL - * Serverdatetime values. When false, java.sql.Time values will be sent to the server as SQL Servertime values. sendTimeAsDatetime can also be - * modified programmatically with SQLServerDataSource.setSendTimeAsDatetime. The default value for this property may change in a future release. - * - * @param sendTimeAsDateTimeValue - * enables/disables setting the sendTimeAsDatetime connection property. For more information about how the Microsoft JDBC Driver for - * SQL Server configures java.sql.Time values before sending them to the server, see - * Configuring How java.sql.Time Values are Sent to the - * Server. - */ - public synchronized void setSendTimeAsDatetime(boolean sendTimeAsDateTimeValue) { + @Override + public void setSendTimeAsDatetime(boolean sendTimeAsDateTimeValue) { sendTimeAsDatetime = sendTimeAsDateTimeValue; } + @Override public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException { - // Not implemented - throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + SQLServerException.throwNotSupportedException(this, null); + return null; } - public Blob createBlob() throws SQLException { + @Override + public java.sql.Blob createBlob() throws SQLException { checkClosed(); return new SQLServerBlob(this); } - public Clob createClob() throws SQLException { + @Override + public java.sql.Clob createClob() throws SQLException { checkClosed(); return new SQLServerClob(this); } - public NClob createNClob() throws SQLException { + @Override + public java.sql.NClob createNClob() throws SQLException { checkClosed(); return new SQLServerNClob(this); } + @Override public SQLXML createSQLXML() throws SQLException { loggerExternal.entering(getClassNameLogging(), "createSQLXML"); - SQLXML sqlxml = new SQLServerSQLXML(this); + SQLXML sqlxml = new SQLServerSQLXML(this); if (loggerExternal.isLoggable(Level.FINER)) loggerExternal.exiting(getClassNameLogging(), "createSQLXML", sqlxml); return sqlxml; } - public Struct createStruct(String typeName, + @Override + public java.sql.Struct createStruct(String typeName, Object[] attributes) throws SQLException { - // Not implemented - throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + SQLServerException.throwNotSupportedException(this, null); + return null; } String getTrustedServerNameAE() throws SQLServerException { return trustedServerNameAE.toUpperCase(); } + @Override public Properties getClientInfo() throws SQLException { loggerExternal.entering(getClassNameLogging(), "getClientInfo"); checkClosed(); @@ -5282,6 +5134,7 @@ public Properties getClientInfo() throws SQLException { return p; } + @Override public String getClientInfo(String name) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getClientInfo", name); checkClosed(); @@ -5289,6 +5142,7 @@ public String getClientInfo(String name) throws SQLException { return null; } + @Override public void setClientInfo(Properties properties) throws SQLClientInfoException { loggerExternal.entering(getClassNameLogging(), "setClientInfo", properties); // This function is only marked as throwing only SQLClientInfoException so the conversion is necessary @@ -5312,6 +5166,7 @@ public void setClientInfo(Properties properties) throws SQLClientInfoException { loggerExternal.exiting(getClassNameLogging(), "setClientInfo"); } + @Override public void setClientInfo(String name, String value) throws SQLClientInfoException { loggerExternal.entering(getClassNameLogging(), "setClientInfo", new Object[] {name, value}); @@ -5348,6 +5203,7 @@ public void setClientInfo(String name, * @throws SQLException * if the value supplied for the timeout is less than 0. */ + @Override public boolean isValid(int timeout) throws SQLException { boolean isValid = false; @@ -5391,6 +5247,7 @@ public boolean isValid(int timeout) throws SQLException { return isValid; } + @Override public boolean isWrapperFor(Class iface) throws SQLException { loggerExternal.entering(getClassNameLogging(), "isWrapperFor", iface); boolean f = iface.isInstance(this); @@ -5398,6 +5255,7 @@ public boolean isWrapperFor(Class iface) throws SQLException { return f; } + @Override public T unwrap(Class iface) throws SQLException { loggerExternal.entering(getClassNameLogging(), "unwrap", iface); T t; @@ -5428,6 +5286,7 @@ public T unwrap(Class iface) throws SQLException { private List openStatements; protected void beginRequestInternal() throws SQLException { + loggerExternal.entering(getClassNameLogging(), "beginRequest", this); synchronized (this) { if (!requestStarted) { originalDatabaseAutoCommitMode = databaseAutoCommitMode; @@ -5445,9 +5304,11 @@ protected void beginRequestInternal() throws SQLException { requestStarted = true; } } + loggerExternal.exiting(getClassNameLogging(), "beginRequest", this); } protected void endRequestInternal() throws SQLException { + loggerExternal.entering(getClassNameLogging(), "endRequest", this); synchronized (this) { if (requestStarted) { if (!databaseAutoCommitMode) { @@ -5494,6 +5355,7 @@ protected void endRequestInternal() throws SQLException { requestStarted = false; } } + loggerExternal.exiting(getClassNameLogging(), "endRequest", this); } /** @@ -5506,7 +5368,7 @@ protected void endRequestInternal() throws SQLException { */ static final char[] OUT = {' ', 'O', 'U', 'T'}; - /* L0 */ String replaceParameterMarkers(String sqlSrc, + String replaceParameterMarkers(String sqlSrc, Parameter[] params, boolean isReturnValueSyntax) throws SQLServerException { final int MAX_PARAM_NAME_LEN = 6; @@ -5551,7 +5413,7 @@ protected void endRequestInternal() throws SQLException { * @param offset * @return int */ - /* L0 */ static int makeParamName(int nParam, + static int makeParamName(int nParam, char[] name, int offset) { name[offset + 0] = '@'; @@ -5689,8 +5551,7 @@ String getInstancePort(String server, datagramSocket.receive(udpResponse); browserResult = new String(receiveBuffer, 3, receiveBuffer.length - 3); if (connectionlogger.isLoggable(Level.FINER)) - connectionlogger.fine( - toString() + " Received SSRP UDP response from IP address: " + udpResponse.getAddress().getHostAddress()); + connectionlogger.fine(toString() + " Received SSRP UDP response from IP address: " + udpResponse.getAddress().getHostAddress()); } catch (IOException ioException) { // Warn and retry @@ -5724,7 +5585,7 @@ String getInstancePort(String server, return browserResult.substring(p1, p2); } - /* L0 */ int getNextSavepointId() { + int getNextSavepointId() { nNextSavePointId++; // Make them unique for this connection return nNextSavePointId; } @@ -5765,16 +5626,15 @@ public static synchronized void setColumnEncryptionKeyCacheTtl(int columnEncrypt static synchronized long getColumnEncryptionKeyCacheTtl() { return columnEncryptionKeyCacheTtl; } - /** * Enqueue a discarded prepared statement handle to be clean-up on the server. * * @param statementHandle - * The prepared statement handle that should be scheduled for unprepare. + * The prepared statement handle that should be scheduled for unprepare. */ final void enqueueUnprepareStatementHandle(PreparedStatementHandle statementHandle) { - if(null == statementHandle) + if (null == statementHandle) return; if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -5785,19 +5645,12 @@ final void enqueueUnprepareStatementHandle(PreparedStatementHandle statementHand this.discardedPreparedStatementHandleCount.incrementAndGet(); } - - /** - * Returns the number of currently outstanding prepared statement un-prepare actions. - * - * @return Returns the current value per the description. - */ + @Override public int getDiscardedServerPreparedStatementCount() { return this.discardedPreparedStatementHandleCount.get(); } - /** - * Forces the un-prepare requests for any outstanding discarded prepared statements to be executed. - */ + @Override public void closeUnreferencedPreparedStatementHandles() { this.unprepareUnreferencedPreparedStatementHandles(true); } @@ -5810,43 +5663,20 @@ private final void cleanupPreparedStatementDiscardActions() { discardedPreparedStatementHandleCount.set(0); } - /** - * Returns the behavior for a specific connection instance. If false the first execution will call sp_executesql and not prepare - * a statement, once the second execution happens it will call sp_prepexec and actually setup a prepared statement handle. Following - * executions will call sp_execute. This relieves the need for sp_unprepare on prepared statement close if the statement is only - * executed once. The default for this option can be changed by calling setDefaultEnablePrepareOnFirstPreparedStatementCall(). - * - * @return Returns the current setting per the description. - */ + @Override public boolean getEnablePrepareOnFirstPreparedStatementCall() { - if(null == this.enablePrepareOnFirstPreparedStatementCall) + if (null == this.enablePrepareOnFirstPreparedStatementCall) return DEFAULT_ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT_CALL; else - return this.enablePrepareOnFirstPreparedStatementCall; + return this.enablePrepareOnFirstPreparedStatementCall; } - /** - * Specifies the behavior for a specific connection instance. If value is false the first execution will call sp_executesql and not prepare - * a statement, once the second execution happens it will call sp_prepexec and actually setup a prepared statement handle. Following - * executions will call sp_execute. This relieves the need for sp_unprepare on prepared statement close if the statement is only - * executed once. - * - * @param value - * Changes the setting per the description. - */ + @Override public void setEnablePrepareOnFirstPreparedStatementCall(boolean value) { this.enablePrepareOnFirstPreparedStatementCall = value; } - /** - * Returns the behavior for a specific connection instance. This setting controls how many outstanding prepared statement discard actions - * (sp_unprepare) can be outstanding per connection before a call to clean-up the outstanding handles on the server is executed. If the setting is - * {@literal <=} 1, unprepare actions will be executed immedietely on prepared statement close. If it is set to {@literal >} 1, these calls - * will be batched together to avoid overhead of calling sp_unprepare too often. The default for this option can be changed by calling - * getDefaultServerPreparedStatementDiscardThreshold(). - * - * @return Returns the current setting per the description. - */ + @Override public int getServerPreparedStatementDiscardThreshold() { if (0 > this.serverPreparedStatementDiscardThreshold) return DEFAULT_SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD; @@ -5854,32 +5684,24 @@ public int getServerPreparedStatementDiscardThreshold() { return this.serverPreparedStatementDiscardThreshold; } - /** - * Specifies the behavior for a specific connection instance. This setting controls how many outstanding prepared statement discard actions - * (sp_unprepare) can be outstanding per connection before a call to clean-up the outstanding handles on the server is executed. If the setting is - * {@literal <=} 1 unprepare actions will be executed immedietely on prepared statement close. If it is set to {@literal >} 1 these calls will be - * batched together to avoid overhead of calling sp_unprepare too often. - * - * @param value - * Changes the setting per the description. - */ + @Override public void setServerPreparedStatementDiscardThreshold(int value) { this.serverPreparedStatementDiscardThreshold = Math.max(0, value); } final boolean isPreparedStatementUnprepareBatchingEnabled() { - return 1 < getServerPreparedStatementDiscardThreshold(); + return 1 < getServerPreparedStatementDiscardThreshold(); } /** * Cleans-up discarded prepared statement handles on the server using batched un-prepare actions if the batching threshold has been reached. * - * @param force - * When force is set to true we ignore the current threshold for if the discard actions should run and run them anyway. + * @param force + * When force is set to true we ignore the current threshold for if the discard actions should run and run them anyway. */ final void unprepareUnreferencedPreparedStatementHandles(boolean force) { // Skip out if session is unavailable to adhere to previous non-batched behavior. - if (isSessionUnAvailable()) + if (isSessionUnAvailable()) return; final int threshold = getServerPreparedStatementDiscardThreshold(); @@ -5888,33 +5710,32 @@ final void unprepareUnreferencedPreparedStatementHandles(boolean force) { if (force || threshold < getDiscardedServerPreparedStatementCount()) { // Create batch of sp_unprepare statements. - StringBuilder sql = new StringBuilder(threshold * 32/*EXEC sp_cursorunprepare++;*/); + StringBuilder sql = new StringBuilder(threshold * 32/* EXEC sp_cursorunprepare++; */); // Build the string containing no more than the # of handles to remove. - // Note that sp_unprepare can fail if the statement is already removed. - // However, the server will only abort that statement and continue with + // Note that sp_unprepare can fail if the statement is already removed. + // However, the server will only abort that statement and continue with // the remaining clean-up. int handlesRemoved = 0; PreparedStatementHandle statementHandle = null; - while (null != (statementHandle = discardedPreparedStatementHandles.poll())){ + while (null != (statementHandle = discardedPreparedStatementHandles.poll())) { ++handlesRemoved; - - sql.append(statementHandle.isDirectSql() ? "EXEC sp_unprepare " : "EXEC sp_cursorunprepare ") - .append(statementHandle.getHandle()) - .append(';'); + + sql.append(statementHandle.isDirectSql() ? "EXEC sp_unprepare " : "EXEC sp_cursorunprepare ").append(statementHandle.getHandle()) + .append(';'); } try { // Execute the batched set. - try(Statement stmt = this.createStatement()) { + try (Statement stmt = this.createStatement()) { stmt.execute(sql.toString()); } if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.finer(this + ": Finished un-preparing handle count:" + handlesRemoved); } - catch(SQLException e) { + catch (SQLException e) { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.log(Level.FINER, this + ": Error batch-closing at least one prepared handle", e); } @@ -5924,61 +5745,38 @@ final void unprepareUnreferencedPreparedStatementHandles(boolean force) { } } - /** - * Determine whether statement pooling is disabled. - * - * @return true if statement pooling is disabled, false if it is enabled. - */ + @Override public boolean getDisableStatementPooling() { return this.disableStatementPooling; } - /** - * Disable/enable statement pooling. - * - * @param value true to disable statement pooling, false to enable it. - */ + @Override public void setDisableStatementPooling(boolean value) { this.disableStatementPooling = value; if (!value && 0 < this.getStatementPoolingCacheSize()) { prepareCache(); } } - - /** - * Returns the size of the prepared statement cache for this connection. A value less than 1 means no cache. - * @return Returns the current setting per the description. - */ + + @Override public int getStatementPoolingCacheSize() { return statementPoolingCacheSize; - } + } - /** - * Returns the current number of pooled prepared statement handles. - * @return Returns the current setting per the description. - */ + @Override public int getStatementHandleCacheEntryCount() { - if(!isStatementPoolingEnabled()) + if (!isStatementPoolingEnabled()) return 0; else return this.preparedStatementHandleCache.size(); } - /** - * Whether statement pooling is enabled or not for this connection. - * @return Returns the current setting per the description. - */ + @Override public boolean isStatementPoolingEnabled() { return null != preparedStatementHandleCache && 0 < this.getStatementPoolingCacheSize() && !this.getDisableStatementPooling(); } - /** - * Specifies the size of the prepared statement cache for this connection. A value less than 1 means no cache. - * - * @param value - * The new cache size. - * - */ + @Override public void setStatementPoolingCacheSize(int value) { value = Math.max(0, value); statementPoolingCacheSize = value; @@ -5995,6 +5793,7 @@ public void setStatementPoolingCacheSize(int value) { /** * Internal method to prepare the cache handle + * * @param value */ private void prepareCache() { @@ -6007,33 +5806,36 @@ private void prepareCache() { /** Get a parameter metadata cache entry if statement pooling is enabled */ final SQLServerParameterMetaData getCachedParameterMetadata(Sha1HashKey key) { - if(!isStatementPoolingEnabled()) + if (!isStatementPoolingEnabled()) return null; - + return parameterMetadataCache.get(key); } /** Register a parameter metadata cache entry if statement pooling is enabled */ - final void registerCachedParameterMetadata(Sha1HashKey key, SQLServerParameterMetaData pmd) { - if(!isStatementPoolingEnabled() || null == pmd) + final void registerCachedParameterMetadata(Sha1HashKey key, + SQLServerParameterMetaData pmd) { + if (!isStatementPoolingEnabled() || null == pmd) return; - + parameterMetadataCache.put(key, pmd); } /** Get or create prepared statement handle cache entry if statement pooling is enabled */ final PreparedStatementHandle getCachedPreparedStatementHandle(Sha1HashKey key) { - if(!isStatementPoolingEnabled()) + if (!isStatementPoolingEnabled()) return null; - + return preparedStatementHandleCache.get(key); } /** Get or create prepared statement handle cache entry if statement pooling is enabled */ - final PreparedStatementHandle registerCachedPreparedStatementHandle(Sha1HashKey key, int handle, boolean isDirectSql) { - if(!isStatementPoolingEnabled() || null == key) + final PreparedStatementHandle registerCachedPreparedStatementHandle(Sha1HashKey key, + int handle, + boolean isDirectSql) { + if (!isStatementPoolingEnabled() || null == key) return null; - + PreparedStatementHandle cacheItem = new PreparedStatementHandle(key, handle, isDirectSql, false); preparedStatementHandleCache.putIfAbsent(key, cacheItem); return cacheItem; @@ -6049,23 +5851,24 @@ final void returnCachedPreparedStatementHandle(PreparedStatementHandle handle) { /** Force eviction of prepared statement handle cache entry. */ final void evictCachedPreparedStatementHandle(PreparedStatementHandle handle) { - if(null == handle || null == handle.getKey()) - return; - - preparedStatementHandleCache.remove(handle.getKey()); + if (null == handle || null == handle.getKey()) + return; + + preparedStatementHandleCache.remove(handle.getKey()); } // Handle closing handles when removed from cache. final class PreparedStatementCacheEvictionListener implements EvictionListener { - public void onEviction(Sha1HashKey key, PreparedStatementHandle handle) { - if(null != handle) { + public void onEviction(Sha1HashKey key, + PreparedStatementHandle handle) { + if (null != handle) { handle.setIsEvictedFromCache(true); // Mark as evicted from cache. // Only discard if not referenced. - if(handle.tryDiscardHandle()) { + if (handle.tryDiscardHandle()) { enqueueUnprepareStatementHandle(handle); // Do not run discard actions here! Can interfere with executing statement. - } + } } } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java index e48d6c6ac..27c782c0c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java @@ -1,91 +1,64 @@ /* * Microsoft JDBC Driver for SQL Server - * + * * Copyright(c) Microsoft Corporation All rights reserved. - * + * * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. */ package com.microsoft.sqlserver.jdbc; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; import java.sql.ShardingKey; +/** + * SQLServerConnection43 extends {@link SQLServerConnection43} class and implements {@link ISQLServerConnection43} with methods introduced in JDBC 4.3 + * Specifications. This class is used by the drdiver when initializing a class with 43 driver version + */ public class SQLServerConnection43 extends SQLServerConnection implements ISQLServerConnection43 { + /** + * Always refresh SerialVersionUID when prompted + */ + private static final long serialVersionUID = -6904163521498951547L; + SQLServerConnection43(String parentInfo) throws SQLServerException { super(parentInfo); } - /** - * Hints to the driver that a request, an independent unit of work, is beginning on this connection. It backs up the values of the connection - * properties that are modifiable through public methods. Each request is independent of all other requests with regard to state local to the - * connection either on the client or the server. Work done between {@code beginRequest}, {@code endRequest} pairs does not depend on any other - * work done on the connection either as part of another request or outside of any request. A request may include multiple transactions. There may - * be dependencies on committed database state as that is not local to the connection. {@code beginRequest} marks the beginning of the work unit. - *

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

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

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

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

- * - * @throws SQLException - * if an error occurs - * @see beginRequest - */ + @Override public void endRequest() throws SQLException { endRequestInternal(); } - public void setShardingKey(ShardingKey shardingKey) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLServerException("setShardingKey not implemented", new SQLFeatureNotSupportedException("setShardingKey not implemented")); + @Override + public void setShardingKey(ShardingKey shardingKey) throws SQLException { + SQLServerException.throwFeatureNotSupportedException(); } + @Override public void setShardingKey(ShardingKey shardingKey, - ShardingKey superShardingKey) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLServerException("setShardingKey not implemented", new SQLFeatureNotSupportedException("setShardingKey not implemented")); + ShardingKey superShardingKey) throws SQLException { + SQLServerException.throwFeatureNotSupportedException(); } + @Override public boolean setShardingKeyIfValid(ShardingKey shardingKey, - int timeout) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLServerException("setShardingKeyIfValid not implemented", - new SQLFeatureNotSupportedException("setShardingKeyIfValid not implemented")); + int timeout) throws SQLException { + SQLServerException.throwFeatureNotSupportedException(); + return false; } + @Override public boolean setShardingKeyIfValid(ShardingKey shardingKey, ShardingKey superShardingKey, - int timeout) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLServerException("setShardingKeyIfValid not implemented", - new SQLFeatureNotSupportedException("setShardingKeyIfValid not implemented")); + int timeout) throws SQLException { + SQLServerException.throwFeatureNotSupportedException(); + return false; } - } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionPoolDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionPoolDataSource.java index 798a6b036..50115cd05 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionPoolDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionPoolDataSource.java @@ -21,10 +21,10 @@ * physical connections. For example, J2EE application servers that provide JDBC 3.0 API spec connection pooling. * */ - public class SQLServerConnectionPoolDataSource extends SQLServerDataSource implements ConnectionPoolDataSource { // Get a new physical connection that the pool manager will issue logical connections from - /* L0 */ public PooledConnection getPooledConnection() throws SQLException { + @Override + public PooledConnection getPooledConnection() throws SQLException { if (loggerExternal.isLoggable(Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getPooledConnection"); PooledConnection pcon = getPooledConnection(getUser(), getPassword()); @@ -33,7 +33,8 @@ public class SQLServerConnectionPoolDataSource extends SQLServerDataSource imple return pcon; } - /* L0 */ public PooledConnection getPooledConnection(String user, + @Override + public PooledConnection getPooledConnection(String user, String password) throws SQLException { if (loggerExternal.isLoggable(Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getPooledConnection", new Object[] {user, "Password not traced"}); @@ -45,6 +46,7 @@ public class SQLServerConnectionPoolDataSource extends SQLServerDataSource imple // Implement javax.naming.Referenceable interface methods. + @Override public Reference getReference() { if (loggerExternal.isLoggable(Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getReference"); @@ -82,5 +84,4 @@ private Object readResolve() { return ds; } } - } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionPoolProxy.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionPoolProxy.java index 80d3a234d..cca04ad76 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionPoolProxy.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionPoolProxy.java @@ -1,689 +1,615 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.jdbc; - -import java.sql.Blob; -import java.sql.CallableStatement; -import java.sql.Clob; -import java.sql.DatabaseMetaData; -import java.sql.NClob; -import java.sql.PreparedStatement; -import java.sql.SQLClientInfoException; -import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; -import java.sql.SQLPermission; -import java.sql.SQLWarning; -import java.sql.SQLXML; -import java.sql.Savepoint; -import java.sql.Statement; -import java.sql.Struct; -import java.text.MessageFormat; -import java.util.Properties; -import java.util.UUID; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Level; - -/** - * SQLServerConnectionPoolProxy is a wrapper around SQLServerConnection object. When returning a connection object from PooledConnection.getConnection - * we return this proxy per SPEC. - *

- * This class's public functions need to be kept identical to the SQLServerConnection's. - *

- * The API javadoc for JDBC API methods that this class implements are not repeated here. Please see Sun's JDBC API interfaces javadoc for those - * details. - */ - -class SQLServerConnectionPoolProxy implements ISQLServerConnection { - private SQLServerConnection wrappedConnection; - private boolean bIsOpen; - static private final AtomicInteger baseConnectionID = new AtomicInteger(0); // connection id dispenser - final private String traceID; - - // Permission targets - // currently only callAbort is implemented - private static final String callAbortPerm = "callAbort"; - - /** - * Generate the next unique connection id. - * - * @return the next conn id - */ - /* L0 */ private static int nextConnectionID() { - return baseConnectionID.incrementAndGet(); - } - - public String toString() { - return traceID; - } - - /* L0 */ SQLServerConnectionPoolProxy(SQLServerConnection con) { - traceID = " ProxyConnectionID:" + nextConnectionID(); - wrappedConnection = con; - // the Proxy is created with an open conn - con.setAssociatedProxy(this); - bIsOpen = true; - } - - /* L0 */ void checkClosed() throws SQLServerException { - if (!bIsOpen) { - SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_connectionIsClosed"), null, false); - } - } - - /* L0 */ public Statement createStatement() throws SQLServerException { - checkClosed(); - return wrappedConnection.createStatement(); - } - - /* L0 */ public PreparedStatement prepareStatement(String sql) throws SQLServerException { - checkClosed(); - return wrappedConnection.prepareStatement(sql); - } - - /* L0 */ public CallableStatement prepareCall(String sql) throws SQLServerException { - checkClosed(); - return wrappedConnection.prepareCall(sql); - } - - /* L0 */ public String nativeSQL(String sql) throws SQLServerException { - checkClosed(); - return wrappedConnection.nativeSQL(sql); - } - - public void setAutoCommit(boolean newAutoCommitMode) throws SQLServerException { - checkClosed(); - wrappedConnection.setAutoCommit(newAutoCommitMode); - } - - /* L0 */ public boolean getAutoCommit() throws SQLServerException { - checkClosed(); - return wrappedConnection.getAutoCommit(); - } - - public void commit() throws SQLServerException { - checkClosed(); - wrappedConnection.commit(); - } - - /** - * Rollback a transaction. - * - * @throws SQLServerException - * if no transaction exists or if the connection is in auto-commit mode. - */ - public void rollback() throws SQLServerException { - checkClosed(); - wrappedConnection.rollback(); - } - - public void abort(Executor executor) throws SQLException { - if (!bIsOpen || (null == wrappedConnection)) - return; - - if (null == executor) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument")); - Object[] msgArgs = {"executor"}; - SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, false); - } - - // check for callAbort permission - SecurityManager secMgr = System.getSecurityManager(); - if (secMgr != null) { - try { - SQLPermission perm = new SQLPermission(callAbortPerm); - secMgr.checkPermission(perm); - } - catch (SecurityException ex) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_permissionDenied")); - Object[] msgArgs = {callAbortPerm}; - throw new SQLServerException(form.format(msgArgs), null, 0, ex); - } - } - - bIsOpen = false; - - executor.execute(new Runnable() { - public void run() { - if (wrappedConnection.getConnectionLogger().isLoggable(Level.FINER)) - wrappedConnection.getConnectionLogger().finer(toString() + " Connection proxy aborted "); - try { - wrappedConnection.poolCloseEventNotify(); - wrappedConnection = null; - } - catch (SQLException e) { - throw new RuntimeException(e); - } - } - }); - } - - /* L0 */ public void close() throws SQLServerException { - if (bIsOpen && (null != wrappedConnection)) { - if (wrappedConnection.getConnectionLogger().isLoggable(Level.FINER)) - wrappedConnection.getConnectionLogger().finer(toString() + " Connection proxy closed "); - - wrappedConnection.poolCloseEventNotify(); - wrappedConnection = null; - } - bIsOpen = false; - } - - /* L0 */ void internalClose() { - bIsOpen = false; - wrappedConnection = null; - } - - /* L0 */ public boolean isClosed() throws SQLServerException { - return !bIsOpen; - } - - /* L0 */ public DatabaseMetaData getMetaData() throws SQLServerException { - checkClosed(); - return wrappedConnection.getMetaData(); - } - - /* L0 */ public void setReadOnly(boolean readOnly) throws SQLServerException { - checkClosed(); - wrappedConnection.setReadOnly(readOnly); - } - - /* L0 */ public boolean isReadOnly() throws SQLServerException { - checkClosed(); - return wrappedConnection.isReadOnly(); - } - - /* L0 */ public void setCatalog(String catalog) throws SQLServerException { - checkClosed(); - wrappedConnection.setCatalog(catalog); - } - - /* L0 */ public String getCatalog() throws SQLServerException { - checkClosed(); - return wrappedConnection.getCatalog(); - } - - /* L0 */ public void setTransactionIsolation(int level) throws SQLServerException { - checkClosed(); - wrappedConnection.setTransactionIsolation(level); - } - - /* L0 */ public int getTransactionIsolation() throws SQLServerException { - checkClosed(); - return wrappedConnection.getTransactionIsolation(); - } - - /* L0 */ public SQLWarning getWarnings() throws SQLServerException { - checkClosed(); - return wrappedConnection.getWarnings(); // Warnings support added - } - - /* L2 */ public void clearWarnings() throws SQLServerException { - checkClosed(); - wrappedConnection.clearWarnings(); - } - - // --------------------------JDBC 2.0----------------------------- - - /* L2 */ public Statement createStatement(int resultSetType, - int resultSetConcurrency) throws SQLException { - checkClosed(); - return wrappedConnection.createStatement(resultSetType, resultSetConcurrency); - } - - /* L2 */ public PreparedStatement prepareStatement(String sSql, - int resultSetType, - int resultSetConcurrency) throws SQLException { - checkClosed(); - return wrappedConnection.prepareStatement(sSql, resultSetType, resultSetConcurrency); - } - - /* L2 */ public CallableStatement prepareCall(String sql, - int resultSetType, - int resultSetConcurrency) throws SQLException { - checkClosed(); - return wrappedConnection.prepareCall(sql, resultSetType, resultSetConcurrency); - } - - /* L2 */ public void setTypeMap(java.util.Map> map) throws SQLServerException { - checkClosed(); - wrappedConnection.setTypeMap(map); - } - - public java.util.Map> getTypeMap() throws SQLServerException { - checkClosed(); - return wrappedConnection.getTypeMap(); - } - - /* L3 */ public Statement createStatement(int nType, - int nConcur, - int nHold) throws SQLServerException { - checkClosed(); - return wrappedConnection.createStatement(nType, nConcur, nHold); - } - - /** - * Creates a Statement object that will generate ResultSet objects with the given type, concurrency, and holdability. - * This method is the same as the createStatement method above, but it allows the default result set type, concurrency, and - * holdability to be overridden. - * - * @param nType - * one of the following ResultSet constants: ResultSet.TYPE_FORWARD_ONLY, - * ResultSet.TYPE_SCROLL_INSENSITIVE, or ResultSet.TYPE_SCROLL_SENSITIVE - * @param nConcur - * one of the following ResultSet constants: ResultSet.CONCUR_READ_ONLY or - * ResultSet.CONCUR_UPDATABLE - * @param nHold - * one of the following ResultSet constants: ResultSet.HOLD_CURSORS_OVER_COMMIT or - * ResultSet.CLOSE_CURSORS_AT_COMMIT - * @param stmtColEncSetting - * Specifies how data will be sent and received when reading and writing encrypted columns. - * @return a new Statement object that will generate ResultSet objects with the given type, concurrency, and holdability - * @exception SQLException - * if a database access error occurs, this method is called on a closed connection or the given parameters are not - * ResultSet constants indicating type, concurrency, and holdability - * @exception SQLFeatureNotSupportedException - * if the JDBC driver does not support this method or this method is not supported for the specified result set type, result set - * holdability and result set concurrency. - */ - public Statement createStatement(int nType, - int nConcur, - int nHold, - SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { - checkClosed(); - return wrappedConnection.createStatement(nType, nConcur, nHold, stmtColEncSetting); - } - - /* L3 */ public PreparedStatement prepareStatement(java.lang.String sql, - int nType, - int nConcur, - int nHold) throws SQLServerException { - checkClosed(); - return wrappedConnection.prepareStatement(sql, nType, nConcur, nHold); - } - - /** - * Creates a PreparedStatement object that will generate ResultSet objects with the given type, concurrency, and - * holdability. - *

- * This method is the same as the prepareStatement method above, but it allows the default result set type, concurrency, and - * holdability to be overridden. - * - * @param sql - * a String object that is the SQL statement to be sent to the database; may contain one or more '?' IN parameters - * @param nType - * one of the following ResultSet constants: ResultSet.TYPE_FORWARD_ONLY, - * ResultSet.TYPE_SCROLL_INSENSITIVE, or ResultSet.TYPE_SCROLL_SENSITIVE - * @param nConcur - * one of the following ResultSet constants: ResultSet.CONCUR_READ_ONLY or - * ResultSet.CONCUR_UPDATABLE - * @param nHold - * one of the following ResultSet constants: ResultSet.HOLD_CURSORS_OVER_COMMIT or - * ResultSet.CLOSE_CURSORS_AT_COMMIT - * @param stmtColEncSetting - * Specifies how data will be sent and received when reading and writing encrypted columns. - * @return a new PreparedStatement object, containing the pre-compiled SQL statement, that will generate ResultSet - * objects with the given type, concurrency, and holdability - * @exception SQLException - * if a database access error occurs, this method is called on a closed connection or the given parameters are not - * ResultSet constants indicating type, concurrency, and holdability - * @exception SQLFeatureNotSupportedException - * if the JDBC driver does not support this method or this method is not supported for the specified result set type, result set - * holdability and result set concurrency. - */ - public PreparedStatement prepareStatement(String sql, - int nType, - int nConcur, - int nHold, - SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { - checkClosed(); - return wrappedConnection.prepareStatement(sql, nType, nConcur, nHold, stmtColEncSetting); - } - - /* L3 */ public CallableStatement prepareCall(String sql, - int nType, - int nConcur, - int nHold) throws SQLServerException { - checkClosed(); - return wrappedConnection.prepareCall(sql, nType, nConcur, nHold); - } - - /** - * Creates a CallableStatement object that will generate ResultSet objects with the given type and concurrency. This - * method is the same as the prepareCall method above, but it allows the default result set type, result set concurrency type and - * holdability to be overridden. - * - * @param sql - * a String object that is the SQL statement to be sent to the database; may contain on or more '?' parameters - * @param nType - * one of the following ResultSet constants: ResultSet.TYPE_FORWARD_ONLY, - * ResultSet.TYPE_SCROLL_INSENSITIVE, or ResultSet.TYPE_SCROLL_SENSITIVE - * @param nConcur - * one of the following ResultSet constants: ResultSet.CONCUR_READ_ONLY or - * ResultSet.CONCUR_UPDATABLE - * @param nHold - * one of the following ResultSet constants: ResultSet.HOLD_CURSORS_OVER_COMMIT or - * ResultSet.CLOSE_CURSORS_AT_COMMIT - * @param stmtColEncSetting - * Specifies how data will be sent and received when reading and writing encrypted columns. - * @return a new CallableStatement object, containing the pre-compiled SQL statement, that will generate ResultSet - * objects with the given type, concurrency, and holdability - * @exception SQLException - * if a database access error occurs, this method is called on a closed connection or the given parameters are not - * ResultSet constants indicating type, concurrency, and holdability - * @exception SQLFeatureNotSupportedException - * if the JDBC driver does not support this method or this method is not supported for the specified result set type, result set - * holdability and result set concurrency. - */ - public CallableStatement prepareCall(String sql, - int nType, - int nConcur, - int nHold, - SQLServerStatementColumnEncryptionSetting stmtColEncSetiing) throws SQLServerException { - checkClosed(); - return wrappedConnection.prepareCall(sql, nType, nConcur, nHold, stmtColEncSetiing); - } - - /* JDBC 3.0 Auto generated keys */ - - /* L3 */ public PreparedStatement prepareStatement(String sql, - int flag) throws SQLServerException { - checkClosed(); - return wrappedConnection.prepareStatement(sql, flag); - } - - /** - * Creates a default PreparedStatement object that has the capability to retrieve auto-generated keys. The given constant tells the - * driver whether it should make auto-generated keys available for retrieval. This parameter is ignored if the SQL statement is not an - * INSERT statement, or an SQL statement able to return auto-generated keys (the list of such statements is vendor-specific). - *

- * Note: This method is optimized for handling parametric SQL statements that benefit from precompilation. If the driver supports - * precompilation, the method prepareStatement will send the statement to the database for precompilation. Some drivers may not - * support precompilation. In this case, the statement may not be sent to the database until the PreparedStatement object is - * executed. This has no direct effect on users; however, it does affect which methods throw certain SQLExceptions. - *

- * Result sets created using the returned PreparedStatement object will by default be type TYPE_FORWARD_ONLY and have a - * concurrency level of CONCUR_READ_ONLY. The holdability of the created result sets can be determined by calling - * {@link #getHoldability}. - * - * @param sql - * an SQL statement that may contain one or more '?' IN parameter placeholders - * @param flag - * a flag indicating whether auto-generated keys should be returned; one of Statement.RETURN_GENERATED_KEYS or - * Statement.NO_GENERATED_KEYS - * @param stmtColEncSetting - * Specifies how data will be sent and received when reading and writing encrypted columns. - * @return a new PreparedStatement object, containing the pre-compiled SQL statement, that will have the capability of returning - * auto-generated keys - * @exception SQLException - * if a database access error occurs, this method is called on a closed connection or the given parameter is not a - * Statement constant indicating whether auto-generated keys should be returned - * @exception SQLFeatureNotSupportedException - * if the JDBC driver does not support this method with a constant of Statement.RETURN_GENERATED_KEYS - */ - public PreparedStatement prepareStatement(String sql, - int flag, - SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { - checkClosed(); - return wrappedConnection.prepareStatement(sql, flag, stmtColEncSetting); - } - - /* L3 */ public PreparedStatement prepareStatement(String sql, - int[] columnIndexes) throws SQLServerException { - checkClosed(); - return wrappedConnection.prepareStatement(sql, columnIndexes); - } - - /** - * Creates a default PreparedStatement object capable of returning the auto-generated keys designated by the given array. This array - * contains the indexes of the columns in the target table that contain the auto-generated keys that should be made available. The driver will - * ignore the array if the SQL statement is not an INSERT statement, or an SQL statement able to return auto-generated keys (the list - * of such statements is vendor-specific). - *

- * An SQL statement with or without IN parameters can be pre-compiled and stored in a PreparedStatement object. This object can then - * be used to efficiently execute this statement multiple times. - *

- * Note: This method is optimized for handling parametric SQL statements that benefit from precompilation. If the driver supports - * precompilation, the method prepareStatement will send the statement to the database for precompilation. Some drivers may not - * support precompilation. In this case, the statement may not be sent to the database until the PreparedStatement object is - * executed. This has no direct effect on users; however, it does affect which methods throw certain SQLExceptions. - *

- * Result sets created using the returned PreparedStatement object will by default be type TYPE_FORWARD_ONLY and have a - * concurrency level of CONCUR_READ_ONLY. The holdability of the created result sets can be determined by calling - * {@link #getHoldability}. - * - * @param sql - * an SQL statement that may contain one or more '?' IN parameter placeholders - * @param columnIndexes - * an array of column indexes indicating the columns that should be returned from the inserted row or rows - * @param stmtColEncSetting - * Specifies how data will be sent and received when reading and writing encrypted columns. - * @return a new PreparedStatement object, containing the pre-compiled statement, that is capable of returning the auto-generated - * keys designated by the given array of column indexes - * @exception SQLException - * if a database access error occurs or this method is called on a closed connection - * @exception SQLFeatureNotSupportedException - * if the JDBC driver does not support this method - */ - public PreparedStatement prepareStatement(String sql, - int[] columnIndexes, - SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { - checkClosed(); - return wrappedConnection.prepareStatement(sql, columnIndexes, stmtColEncSetting); - } - - /* L3 */ public PreparedStatement prepareStatement(String sql, - String[] columnNames) throws SQLServerException { - checkClosed(); - return wrappedConnection.prepareStatement(sql, columnNames); - } - - /** - * Creates a default PreparedStatement object capable of returning the auto-generated keys designated by the given array. This array - * contains the names of the columns in the target table that contain the auto-generated keys that should be returned. The driver will ignore the - * array if the SQL statement is not an INSERT statement, or an SQL statement able to return auto-generated keys (the list of such - * statements is vendor-specific). - *

- * An SQL statement with or without IN parameters can be pre-compiled and stored in a PreparedStatement object. This object can then - * be used to efficiently execute this statement multiple times. - *

- * Note: This method is optimized for handling parametric SQL statements that benefit from precompilation. If the driver supports - * precompilation, the method prepareStatement will send the statement to the database for precompilation. Some drivers may not - * support precompilation. In this case, the statement may not be sent to the database until the PreparedStatement object is - * executed. This has no direct effect on users; however, it does affect which methods throw certain SQLExceptions. - *

- * Result sets created using the returned PreparedStatement object will by default be type TYPE_FORWARD_ONLY and have a - * concurrency level of CONCUR_READ_ONLY. The holdability of the created result sets can be determined by calling - * {@link #getHoldability}. - * - * @param sql - * an SQL statement that may contain one or more '?' IN parameter placeholders - * @param columnNames - * an array of column names indicating the columns that should be returned from the inserted row or rows - * @param stmtColEncSetting - * Specifies how data will be sent and received when reading and writing encrypted columns. - * @return a new PreparedStatement object, containing the pre-compiled statement, that is capable of returning the auto-generated - * keys designated by the given array of column names - * @exception SQLException - * if a database access error occurs or this method is called on a closed connection - * @exception SQLFeatureNotSupportedException - * if the JDBC driver does not support this method - */ - public PreparedStatement prepareStatement(String sql, - String[] columnNames, - SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { - checkClosed(); - return wrappedConnection.prepareStatement(sql, columnNames, stmtColEncSetting); - } - - /* JDBC 3.0 Savepoints */ - - /* L3 */ public void releaseSavepoint(Savepoint savepoint) throws SQLServerException { - checkClosed(); - wrappedConnection.releaseSavepoint(savepoint); - } - - /* L3 */ public Savepoint setSavepoint(String sName) throws SQLServerException { - checkClosed(); - return wrappedConnection.setSavepoint(sName); - } - - /* L3 */ public Savepoint setSavepoint() throws SQLServerException { - checkClosed(); - return wrappedConnection.setSavepoint(); - } - - /* L3 */ public void rollback(Savepoint s) throws SQLServerException { - checkClosed(); - wrappedConnection.rollback(s); - } - - /* L3 */ public int getHoldability() throws SQLServerException { - checkClosed(); - return wrappedConnection.getHoldability(); - } - - /* L3 */ public void setHoldability(int nNewHold) throws SQLServerException { - checkClosed(); - wrappedConnection.setHoldability(nNewHold); - } - - public int getNetworkTimeout() throws SQLException { - checkClosed(); - return wrappedConnection.getNetworkTimeout(); - } - - public void setNetworkTimeout(Executor executor, - int timeout) throws SQLException { - checkClosed(); - wrappedConnection.setNetworkTimeout(executor, timeout); - } - - public String getSchema() throws SQLException { - checkClosed(); - return wrappedConnection.getSchema(); - } - - public void setSchema(String schema) throws SQLException { - checkClosed(); - wrappedConnection.setSchema(schema); - } - - public java.sql.Array createArrayOf(String typeName, - Object[] elements) throws SQLException { - checkClosed(); - return wrappedConnection.createArrayOf(typeName, elements); - } - - public Blob createBlob() throws SQLException { - checkClosed(); - return wrappedConnection.createBlob(); - } - - public Clob createClob() throws SQLException { - checkClosed(); - return wrappedConnection.createClob(); - } - - public NClob createNClob() throws SQLException { - checkClosed(); - return wrappedConnection.createNClob(); - } - - public SQLXML createSQLXML() throws SQLException { - checkClosed(); - return wrappedConnection.createSQLXML(); - } - - public Struct createStruct(String typeName, - Object[] attributes) throws SQLException { - checkClosed(); - return wrappedConnection.createStruct(typeName, attributes); - } - - public Properties getClientInfo() throws SQLException { - checkClosed(); - return wrappedConnection.getClientInfo(); - } - - public String getClientInfo(String name) throws SQLException { - checkClosed(); - return wrappedConnection.getClientInfo(name); - } - - public void setClientInfo(Properties properties) throws SQLClientInfoException { - // No checkClosed() call since we can only throw SQLClientInfoException from here - wrappedConnection.setClientInfo(properties); - } - - public void setClientInfo(String name, - String value) throws SQLClientInfoException { - // No checkClosed() call since we can only throw SQLClientInfoException from here - wrappedConnection.setClientInfo(name, value); - } - - public boolean isValid(int timeout) throws SQLException { - checkClosed(); - return wrappedConnection.isValid(timeout); - } - - public boolean isWrapperFor(Class iface) throws SQLException { - wrappedConnection.getConnectionLogger().entering(toString(), "isWrapperFor", iface); - boolean f = iface.isInstance(this); - wrappedConnection.getConnectionLogger().exiting(toString(), "isWrapperFor", f); - return f; - } - - public T unwrap(Class iface) throws SQLException { - wrappedConnection.getConnectionLogger().entering(toString(), "unwrap", iface); - T t; - try { - t = iface.cast(this); - } - catch (ClassCastException e) { - SQLServerException newe = new SQLServerException(e.getMessage(), e); - throw newe; - } - wrappedConnection.getConnectionLogger().exiting(toString(), "unwrap", t); - return t; - } - - public UUID getClientConnectionId() throws SQLServerException { - checkClosed(); - return wrappedConnection.getClientConnectionId(); - } - - /** - * Modifies the setting of the sendTimeAsDatetime connection property. When true, java.sql.Time values will be sent to the server as SQL - * Serverdatetime values. When false, java.sql.Time values will be sent to the server as SQL Servertime values. sendTimeAsDatetime can also be - * modified programmatically with SQLServerDataSource.setSendTimeAsDatetime. The default value for this property may change in a future release. - * - * @param sendTimeAsDateTimeValue - * enables/disables setting the sendTimeAsDatetime connection property. For more information about how the Microsoft JDBC Driver for - * SQL Server configures java.sql.Time values before sending them to the server, see - * Configuring How java.sql.Time Values are Sent to the - * Server. - */ - public synchronized void setSendTimeAsDatetime(boolean sendTimeAsDateTimeValue) throws SQLServerException { - checkClosed(); - wrappedConnection.setSendTimeAsDatetime(sendTimeAsDateTimeValue); - } - - /** - * Returns the setting of the sendTimeAsDatetime connection property. - * - * @return if enabled, returns true. Otherwise, false. - * @throws SQLServerException - * when an error occurs. - */ - public synchronized final boolean getSendTimeAsDatetime() throws SQLServerException { - checkClosed(); - return wrappedConnection.getSendTimeAsDatetime(); - } -} +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + +package com.microsoft.sqlserver.jdbc; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.Savepoint; +import java.sql.Statement; +import java.text.MessageFormat; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * SQLServerConnectionPoolProxy is a wrapper around SQLServerConnection object. When returning a connection object from PooledConnection.getConnection + * we return this proxy per SPEC. + *

+ * This class's public functions need to be kept identical to the SQLServerConnection's. + *

+ * The API javadoc for JDBC API methods that this class implements are not repeated here. Please see Sun's JDBC API interfaces javadoc for those + * details. + */ + +class SQLServerConnectionPoolProxy implements ISQLServerConnection, java.io.Serializable { + /** + * Always refresh SerialVersionUID when prompted + */ + private static final long serialVersionUID = 5752599482349578127L; + + private SQLServerConnection wrappedConnection; + private boolean bIsOpen; + static private final AtomicInteger baseConnectionID = new AtomicInteger(0); // connection + // id + // dispenser + final private String traceID; + + // Permission targets + // currently only callAbort is implemented + private static final String callAbortPerm = "callAbort"; + + /** + * Generate the next unique connection id. + * + * @return the next conn id + */ + private static int nextConnectionID() { + return baseConnectionID.incrementAndGet(); + } + + @Override + public String toString() { + return traceID; + } + + SQLServerConnectionPoolProxy(SQLServerConnection con) { + traceID = " ProxyConnectionID:" + nextConnectionID(); + wrappedConnection = con; + // the Proxy is created with an open conn + con.setAssociatedProxy(this); + bIsOpen = true; + } + + void checkClosed() throws SQLServerException { + if (!bIsOpen) { + SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_connectionIsClosed"), null, false); + } + } + + @Override + public Statement createStatement() throws SQLServerException { + checkClosed(); + return wrappedConnection.createStatement(); + } + + @Override + public PreparedStatement prepareStatement(String sql) throws SQLServerException { + checkClosed(); + return wrappedConnection.prepareStatement(sql); + } + + @Override + public CallableStatement prepareCall(String sql) throws SQLServerException { + checkClosed(); + return wrappedConnection.prepareCall(sql); + } + + @Override + public String nativeSQL(String sql) throws SQLServerException { + checkClosed(); + return wrappedConnection.nativeSQL(sql); + } + + @Override + public void setAutoCommit(boolean newAutoCommitMode) throws SQLServerException { + checkClosed(); + wrappedConnection.setAutoCommit(newAutoCommitMode); + } + + @Override + public boolean getAutoCommit() throws SQLServerException { + checkClosed(); + return wrappedConnection.getAutoCommit(); + } + + @Override + public void commit() throws SQLServerException { + checkClosed(); + wrappedConnection.commit(); + } + + @Override + public void rollback() throws SQLServerException { + checkClosed(); + wrappedConnection.rollback(); + } + + @Override + public void abort(Executor executor) throws SQLException { + if (!bIsOpen || (null == wrappedConnection)) + return; + + if (null == executor) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument")); + Object[] msgArgs = {"executor"}; + SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, false); + } + + // check for callAbort permission + SecurityManager secMgr = System.getSecurityManager(); + if (secMgr != null) { + try { + java.sql.SQLPermission perm = new java.sql.SQLPermission(callAbortPerm); + secMgr.checkPermission(perm); + } + catch (SecurityException ex) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_permissionDenied")); + Object[] msgArgs = {callAbortPerm}; + throw new SQLServerException(form.format(msgArgs), null, 0, ex); + } + } + + bIsOpen = false; + + executor.execute(new Runnable() { + public void run() { + if (wrappedConnection.getConnectionLogger().isLoggable(java.util.logging.Level.FINER)) + wrappedConnection.getConnectionLogger().finer(toString() + " Connection proxy aborted "); + try { + wrappedConnection.poolCloseEventNotify(); + wrappedConnection = null; + } + catch (SQLException e) { + throw new RuntimeException(e); + } + } + }); + } + + @Override + public void close() throws SQLServerException { + if (bIsOpen && (null != wrappedConnection)) { + if (wrappedConnection.getConnectionLogger().isLoggable(java.util.logging.Level.FINER)) + wrappedConnection.getConnectionLogger().finer(toString() + " Connection proxy closed "); + + wrappedConnection.poolCloseEventNotify(); + wrappedConnection = null; + } + bIsOpen = false; + } + + void internalClose() { + bIsOpen = false; + wrappedConnection = null; + } + + @Override + public boolean isClosed() throws SQLServerException { + return !bIsOpen; + } + + @Override + public java.sql.DatabaseMetaData getMetaData() throws SQLServerException { + checkClosed(); + return wrappedConnection.getMetaData(); + } + + @Override + public void setReadOnly(boolean readOnly) throws SQLServerException { + checkClosed(); + wrappedConnection.setReadOnly(readOnly); + } + + @Override + public boolean isReadOnly() throws SQLServerException { + checkClosed(); + return wrappedConnection.isReadOnly(); + } + + @Override + public void setCatalog(String catalog) throws SQLServerException { + checkClosed(); + wrappedConnection.setCatalog(catalog); + } + + @Override + public String getCatalog() throws SQLServerException { + checkClosed(); + return wrappedConnection.getCatalog(); + } + + @Override + public void setTransactionIsolation(int level) throws SQLServerException { + checkClosed(); + wrappedConnection.setTransactionIsolation(level); + } + + @Override + public int getTransactionIsolation() throws SQLServerException { + checkClosed(); + return wrappedConnection.getTransactionIsolation(); + } + + @Override + public java.sql.SQLWarning getWarnings() throws SQLServerException { + checkClosed(); + return wrappedConnection.getWarnings(); // Warnings support added + } + + @Override + public void clearWarnings() throws SQLServerException { + checkClosed(); + wrappedConnection.clearWarnings(); + } + + // --------------------------JDBC 2.0----------------------------- + + @Override + public Statement createStatement(int resultSetType, + int resultSetConcurrency) throws SQLException { + checkClosed(); + return wrappedConnection.createStatement(resultSetType, resultSetConcurrency); + } + + @Override + public PreparedStatement prepareStatement(String sSql, + int resultSetType, + int resultSetConcurrency) throws SQLException { + checkClosed(); + return wrappedConnection.prepareStatement(sSql, resultSetType, resultSetConcurrency); + } + + @Override + public CallableStatement prepareCall(String sql, + int resultSetType, + int resultSetConcurrency) throws SQLException { + checkClosed(); + return wrappedConnection.prepareCall(sql, resultSetType, resultSetConcurrency); + } + + @Override + public void setTypeMap(java.util.Map> map) throws SQLException { + checkClosed(); + wrappedConnection.setTypeMap(map); + } + + @Override + public java.util.Map> getTypeMap() throws SQLServerException { + checkClosed(); + return wrappedConnection.getTypeMap(); + } + + @Override + public Statement createStatement(int nType, + int nConcur, + int nHold) throws SQLServerException { + checkClosed(); + return wrappedConnection.createStatement(nType, nConcur, nHold); + } + + @Override + public Statement createStatement(int nType, + int nConcur, + int nHold, + SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { + checkClosed(); + return wrappedConnection.createStatement(nType, nConcur, nHold, stmtColEncSetting); + } + + @Override + public PreparedStatement prepareStatement(java.lang.String sql, + int nType, + int nConcur, + int nHold) throws SQLServerException { + checkClosed(); + return wrappedConnection.prepareStatement(sql, nType, nConcur, nHold); + } + + @Override + public PreparedStatement prepareStatement(String sql, + int nType, + int nConcur, + int nHold, + SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { + checkClosed(); + return wrappedConnection.prepareStatement(sql, nType, nConcur, nHold, stmtColEncSetting); + } + + @Override + public CallableStatement prepareCall(String sql, + int nType, + int nConcur, + int nHold) throws SQLServerException { + checkClosed(); + return wrappedConnection.prepareCall(sql, nType, nConcur, nHold); + } + + @Override + public CallableStatement prepareCall(String sql, + int nType, + int nConcur, + int nHold, + SQLServerStatementColumnEncryptionSetting stmtColEncSetiing) throws SQLServerException { + checkClosed(); + return wrappedConnection.prepareCall(sql, nType, nConcur, nHold, stmtColEncSetiing); + } + + /* JDBC 3.0 Auto generated keys */ + + @Override + public PreparedStatement prepareStatement(String sql, + int flag) throws SQLServerException { + checkClosed(); + return wrappedConnection.prepareStatement(sql, flag); + } + + @Override + public PreparedStatement prepareStatement(String sql, + int flag, + SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { + checkClosed(); + return wrappedConnection.prepareStatement(sql, flag, stmtColEncSetting); + } + + @Override + public PreparedStatement prepareStatement(String sql, + int[] columnIndexes) throws SQLServerException { + checkClosed(); + return wrappedConnection.prepareStatement(sql, columnIndexes); + } + + @Override + public PreparedStatement prepareStatement(String sql, + int[] columnIndexes, + SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { + checkClosed(); + return wrappedConnection.prepareStatement(sql, columnIndexes, stmtColEncSetting); + } + + @Override + public PreparedStatement prepareStatement(String sql, + String[] columnNames) throws SQLServerException { + checkClosed(); + return wrappedConnection.prepareStatement(sql, columnNames); + } + + @Override + public PreparedStatement prepareStatement(String sql, + String[] columnNames, + SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { + checkClosed(); + return wrappedConnection.prepareStatement(sql, columnNames, stmtColEncSetting); + } + + /* JDBC 3.0 Savepoints */ + + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + checkClosed(); + wrappedConnection.releaseSavepoint(savepoint); + } + + @Override + public Savepoint setSavepoint(String sName) throws SQLServerException { + checkClosed(); + return wrappedConnection.setSavepoint(sName); + } + + @Override + public Savepoint setSavepoint() throws SQLServerException { + checkClosed(); + return wrappedConnection.setSavepoint(); + } + + @Override + public void rollback(Savepoint s) throws SQLServerException { + checkClosed(); + wrappedConnection.rollback(s); + } + + @Override + public int getHoldability() throws SQLServerException { + checkClosed(); + return wrappedConnection.getHoldability(); + } + + @Override + public void setHoldability(int nNewHold) throws SQLServerException { + checkClosed(); + wrappedConnection.setHoldability(nNewHold); + } + + @Override + public int getNetworkTimeout() throws SQLException { + checkClosed(); + return wrappedConnection.getNetworkTimeout(); + } + + @Override + public void setNetworkTimeout(Executor executor, + int timeout) throws SQLException { + checkClosed(); + wrappedConnection.setNetworkTimeout(executor, timeout); + } + + @Override + public String getSchema() throws SQLException { + checkClosed(); + return wrappedConnection.getSchema(); + } + + @Override + public void setSchema(String schema) throws SQLException { + checkClosed(); + wrappedConnection.setSchema(schema); + } + + @Override + public java.sql.Array createArrayOf(String typeName, + Object[] elements) throws SQLException { + checkClosed(); + return wrappedConnection.createArrayOf(typeName, elements); + } + + @Override + public java.sql.Blob createBlob() throws SQLException { + checkClosed(); + return wrappedConnection.createBlob(); + } + + @Override + public java.sql.Clob createClob() throws SQLException { + checkClosed(); + return wrappedConnection.createClob(); + } + + @Override + public java.sql.NClob createNClob() throws SQLException { + checkClosed(); + return wrappedConnection.createNClob(); + } + + @Override + public java.sql.SQLXML createSQLXML() throws SQLException { + checkClosed(); + return wrappedConnection.createSQLXML(); + } + + @Override + public java.sql.Struct createStruct(String typeName, + Object[] attributes) throws SQLException { + checkClosed(); + return wrappedConnection.createStruct(typeName, attributes); + } + + @Override + public java.util.Properties getClientInfo() throws SQLException { + checkClosed(); + return wrappedConnection.getClientInfo(); + } + + @Override + public String getClientInfo(String name) throws SQLException { + checkClosed(); + return wrappedConnection.getClientInfo(name); + } + + @Override + public void setClientInfo(java.util.Properties properties) throws SQLClientInfoException { + // No checkClosed() call since we can only throw SQLClientInfoException + // from here + wrappedConnection.setClientInfo(properties); + } + + @Override + public void setClientInfo(String name, + String value) throws SQLClientInfoException { + // No checkClosed() call since we can only throw SQLClientInfoException + // from here + wrappedConnection.setClientInfo(name, value); + } + + @Override + public boolean isValid(int timeout) throws SQLException { + checkClosed(); + return wrappedConnection.isValid(timeout); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + wrappedConnection.getConnectionLogger().entering(toString(), "isWrapperFor", iface); + boolean f = iface.isInstance(this); + wrappedConnection.getConnectionLogger().exiting(toString(), "isWrapperFor", f); + return f; + } + + @Override + public T unwrap(Class iface) throws SQLException { + wrappedConnection.getConnectionLogger().entering(toString(), "unwrap", iface); + T t; + try { + t = iface.cast(this); + } + catch (ClassCastException e) { + SQLServerException newe = new SQLServerException(e.getMessage(), e); + throw newe; + } + wrappedConnection.getConnectionLogger().exiting(toString(), "unwrap", t); + return t; + } + + @Override + public java.util.UUID getClientConnectionId() throws SQLServerException { + checkClosed(); + return wrappedConnection.getClientConnectionId(); + } + + @Override + public synchronized void setSendTimeAsDatetime(boolean sendTimeAsDateTimeValue) throws SQLServerException { + checkClosed(); + wrappedConnection.setSendTimeAsDatetime(sendTimeAsDateTimeValue); + } + + @Override + public boolean getSendTimeAsDatetime() throws SQLServerException { + checkClosed(); + return wrappedConnection.getSendTimeAsDatetime(); + } + + @Override + public int getDiscardedServerPreparedStatementCount() { + return wrappedConnection.getDiscardedServerPreparedStatementCount(); + } + + @Override + public void closeUnreferencedPreparedStatementHandles() { + wrappedConnection.closeUnreferencedPreparedStatementHandles(); + } + + @Override + public boolean getEnablePrepareOnFirstPreparedStatementCall() { + return wrappedConnection.getEnablePrepareOnFirstPreparedStatementCall(); + } + + @Override + public void setEnablePrepareOnFirstPreparedStatementCall(boolean value) { + wrappedConnection.setEnablePrepareOnFirstPreparedStatementCall(value); + } + + @Override + public int getServerPreparedStatementDiscardThreshold() { + return wrappedConnection.getServerPreparedStatementDiscardThreshold(); + } + + @Override + public void setServerPreparedStatementDiscardThreshold(int value) { + wrappedConnection.setServerPreparedStatementDiscardThreshold(value); + } + + @Override + public void setStatementPoolingCacheSize(int value) { + wrappedConnection.setStatementPoolingCacheSize(value); + } + + @Override + public int getStatementPoolingCacheSize() { + return wrappedConnection.getStatementPoolingCacheSize(); + } + + @Override + public boolean isStatementPoolingEnabled() { + return wrappedConnection.isStatementPoolingEnabled(); + } + + @Override + public int getStatementHandleCacheEntryCount() { + return wrappedConnection.getStatementHandleCacheEntryCount(); + } + + @Override + public void setDisableStatementPooling(boolean value) { + wrappedConnection.setDisableStatementPooling(value); + } + + @Override + public boolean getDisableStatementPooling() { + return wrappedConnection.getDisableStatementPooling(); + } +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java index a01711a12..607c2d89f 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java @@ -11,7 +11,6 @@ import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; import java.util.Enumeration; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; @@ -20,27 +19,39 @@ import javax.naming.Reference; import javax.naming.StringRefAddr; -import javax.sql.DataSource; import org.ietf.jgss.GSSCredential; /** * This datasource lists properties specific for the SQLServerConnection class. */ -public class SQLServerDataSource implements ISQLServerDataSource, DataSource, java.io.Serializable, javax.naming.Referenceable { +public class SQLServerDataSource implements ISQLServerDataSource, javax.sql.DataSource, java.io.Serializable, javax.naming.Referenceable { // dsLogger is logger used for all SQLServerDataSource instances. static final java.util.logging.Logger dsLogger = java.util.logging.Logger.getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerDataSource"); static final java.util.logging.Logger loggerExternal = java.util.logging.Logger.getLogger("com.microsoft.sqlserver.jdbc.DataSource"); static final private java.util.logging.Logger parentLogger = java.util.logging.Logger.getLogger("com.microsoft.sqlserver.jdbc"); final private String loggingClassName; private boolean trustStorePasswordStripped = false; + + /** + * Always refresh SerialVersionUID when prompted + */ private static final long serialVersionUID = 654861379544314296L; - private Properties connectionProps; // Properties passed to SQLServerConnection class. - private String dataSourceURL; // URL for datasource. - private String dataSourceDescription; // Description for datasource. - static private final AtomicInteger baseDataSourceID = new AtomicInteger(0); // Unique id generator for each DataSource instance (used for - // logging). + private Properties connectionProps; // Properties passed to + // SQLServerConnection class. + private String dataSourceURL; // URL for datasource. + private String dataSourceDescription; // Description for datasource. + static private final AtomicInteger baseDataSourceID = new AtomicInteger(0); // Unique + // id + // generator + // for + // each + // DataSource + // instance + // (used + // for + // logging). final private String traceID; /** @@ -58,12 +69,14 @@ String getClassNameLogging() { return loggingClassName; } + @Override public String toString() { return traceID; } // DataSource interface public methods + @Override public Connection getConnection() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getConnection"); Connection con = getConnectionInternal(null, null, null); @@ -71,6 +84,7 @@ public Connection getConnection() throws SQLServerException { return con; } + @Override public Connection getConnection(String username, String password) throws SQLServerException { if (loggerExternal.isLoggable(Level.FINER)) @@ -82,10 +96,12 @@ public Connection getConnection(String username, // Sets the maximum time in seconds that this data source will wait while // attempting to connect to a database. Note default value is 0. + @Override public void setLoginTimeout(int loginTimeout) { setIntProperty(connectionProps, SQLServerDriverIntProperty.LOGIN_TIMEOUT.toString(), loginTimeout); } + @Override public int getLoginTimeout() { int defaultTimeOut = SQLServerDriverIntProperty.LOGIN_TIMEOUT.getDefaultValue(); final int logintimeout = getIntProperty(connectionProps, SQLServerDriverIntProperty.LOGIN_TIMEOUT.toString(), defaultTimeOut); @@ -94,9 +110,11 @@ public int getLoginTimeout() { } // Sets the log writer for this DataSource. - // Currently we just hold onto this logWriter and pass it back to callers, nothing else. + // Currently we just hold onto this logWriter and pass it back to callers, + // nothing else. private transient PrintWriter logWriter; + @Override public void setLogWriter(PrintWriter out) { loggerExternal.entering(getClassNameLogging(), "setLogWriter", out); logWriter = out; @@ -104,298 +122,256 @@ public void setLogWriter(PrintWriter out) { } // Retrieves the log writer for this DataSource. + @Override public PrintWriter getLogWriter() { loggerExternal.entering(getClassNameLogging(), "getLogWriter"); loggerExternal.exiting(getClassNameLogging(), "getLogWriter", logWriter); return logWriter; } - public Logger getParentLogger() throws SQLFeatureNotSupportedException { + @Override + public Logger getParentLogger() throws java.sql.SQLFeatureNotSupportedException { return parentLogger; } // Core Connection property setters/getters. - // applicationName is used to identify the specific application in various SQL Server + // applicationName is used to identify the specific application in various + // SQL Server // profiling and logging tools. + @Override public void setApplicationName(String applicationName) { setStringProperty(connectionProps, SQLServerDriverStringProperty.APPLICATION_NAME.toString(), applicationName); } + @Override public String getApplicationName() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.APPLICATION_NAME.toString(), SQLServerDriverStringProperty.APPLICATION_NAME.getDefaultValue()); } - // databaseName is the name of the database to connect to. If databaseName is not set, + // databaseName is the name of the database to connect to. If databaseName + // is not set, // getDatabaseName returns the default value of null. + @Override public void setDatabaseName(String databaseName) { setStringProperty(connectionProps, SQLServerDriverStringProperty.DATABASE_NAME.toString(), databaseName); } + @Override public String getDatabaseName() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.DATABASE_NAME.toString(), null); } // instanceName is the SQL Server instance name to connect to. - // If instanceName is not set, getInstanceName returns the default value of null. + // If instanceName is not set, getInstanceName returns the default value of + // null. + @Override public void setInstanceName(String instanceName) { setStringProperty(connectionProps, SQLServerDriverStringProperty.INSTANCE_NAME.toString(), instanceName); } + @Override public String getInstanceName() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.INSTANCE_NAME.toString(), null); } + @Override public void setIntegratedSecurity(boolean enable) { setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.INTEGRATED_SECURITY.toString(), enable); } + @Override public void setAuthenticationScheme(String authenticationScheme) { setStringProperty(connectionProps, SQLServerDriverStringProperty.AUTHENTICATION_SCHEME.toString(), authenticationScheme); } - /** - * sets the authentication mode - * - * @param authentication - * the authentication mode - */ + @Override public void setAuthentication(String authentication) { setStringProperty(connectionProps, SQLServerDriverStringProperty.AUTHENTICATION.toString(), authentication); } - /** - * Retrieves the authentication mode - * - * @return the authentication value - */ + @Override public String getAuthentication() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.AUTHENTICATION.toString(), SQLServerDriverStringProperty.AUTHENTICATION.getDefaultValue()); } - /** - * sets GSSCredential - * - * @param userCredential the credential - */ - public void setGSSCredentials(GSSCredential userCredential){ - setObjectProperty(connectionProps,SQLServerDriverObjectProperty.GSS_CREDENTIAL.toString(), userCredential); + @Override + public void setGSSCredentials(GSSCredential userCredential) { + setObjectProperty(connectionProps, SQLServerDriverObjectProperty.GSS_CREDENTIAL.toString(), userCredential); } - /** - * Retrieves the GSSCredential - * - * @return GSSCredential - */ - public GSSCredential getGSSCredentials(){ + @Override + public GSSCredential getGSSCredentials() { return (GSSCredential) getObjectProperty(connectionProps, SQLServerDriverObjectProperty.GSS_CREDENTIAL.toString(), SQLServerDriverObjectProperty.GSS_CREDENTIAL.getDefaultValue()); } - - /** - * Sets the access token. - * - * @param accessToken - * to be set in the string property. - */ + + @Override public void setAccessToken(String accessToken) { setStringProperty(connectionProps, SQLServerDriverStringProperty.ACCESS_TOKEN.toString(), accessToken); } - /** - * Retrieves the access token. - * - * @return the access token. - */ + @Override public String getAccessToken() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.ACCESS_TOKEN.toString(), null); } - // If lastUpdateCount is set to true, the driver will return only the last update - // count from all the update counts returned by a batch. The default of false will - // return all update counts. If lastUpdateCount is not set, getLastUpdateCount + // If lastUpdateCount is set to true, the driver will return only the last + // update + // count from all the update counts returned by a batch. The default of + // false will + // return all update counts. If lastUpdateCount is not set, + // getLastUpdateCount // returns the default value of false. - /** - * Enables/disables Always Encrypted functionality for the data source object. The default is Disabled. - * - * @param columnEncryptionSetting - * Enables/disables Always Encrypted functionality for the data source object. The default is Disabled. - */ + @Override public void setColumnEncryptionSetting(String columnEncryptionSetting) { setStringProperty(connectionProps, SQLServerDriverStringProperty.COLUMN_ENCRYPTION.toString(), columnEncryptionSetting); } - /** - * Retrieves the Always Encrypted functionality setting for the data source object. - * - * @return the Always Encrypted functionality setting for the data source object. - */ + @Override public String getColumnEncryptionSetting() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.COLUMN_ENCRYPTION.toString(), SQLServerDriverStringProperty.COLUMN_ENCRYPTION.getDefaultValue()); } - /** - * Sets the name that identifies a key store. Only value supported is the "JavaKeyStorePassword" for identifying the Java Key Store. The default - * is null. - * - * @param keyStoreAuthentication - * the name that identifies a key store. - */ + @Override public void setKeyStoreAuthentication(String keyStoreAuthentication) { setStringProperty(connectionProps, SQLServerDriverStringProperty.KEY_STORE_AUTHENTICATION.toString(), keyStoreAuthentication); } - /** - * Gets the value of the keyStoreAuthentication setting for the data source object. - * - * @return the value of the keyStoreAuthentication setting for the data source object. - */ + @Override public String getKeyStoreAuthentication() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.KEY_STORE_AUTHENTICATION.toString(), SQLServerDriverStringProperty.KEY_STORE_AUTHENTICATION.getDefaultValue()); } - /** - * Sets the password for the Java keystore. Note that, for Java Key Store provider the password for the keystore and the key must be the same. - * Note that, keyStoreAuthentication must be set with "JavaKeyStorePassword". - * - * @param keyStoreSecret - * the password to use for the keystore as well as for the key - */ + @Override public void setKeyStoreSecret(String keyStoreSecret) { setStringProperty(connectionProps, SQLServerDriverStringProperty.KEY_STORE_SECRET.toString(), keyStoreSecret); } - /** - * Sets the location including the file name for the Java keystore. Note that, keyStoreAuthentication must be set with "JavaKeyStorePassword". - * - * @param keyStoreLocation - * the location including the file name for the Java keystore. - */ + @Override public void setKeyStoreLocation(String keyStoreLocation) { setStringProperty(connectionProps, SQLServerDriverStringProperty.KEY_STORE_LOCATION.toString(), keyStoreLocation); } - /** - * Retrieves the keyStoreLocation for the Java Key Store. - * - * @return the keyStoreLocation for the Java Key Store. - */ + @Override public String getKeyStoreLocation() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.KEY_STORE_LOCATION.toString(), SQLServerDriverStringProperty.KEY_STORE_LOCATION.getDefaultValue()); } + @Override public void setLastUpdateCount(boolean lastUpdateCount) { setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.LAST_UPDATE_COUNT.toString(), lastUpdateCount); } + @Override public boolean getLastUpdateCount() { return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.LAST_UPDATE_COUNT.toString(), SQLServerDriverBooleanProperty.LAST_UPDATE_COUNT.getDefaultValue()); } - // Encryption + @Override public void setEncrypt(boolean encrypt) { setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.ENCRYPT.toString(), encrypt); } + @Override public boolean getEncrypt() { return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.ENCRYPT.toString(), SQLServerDriverBooleanProperty.ENCRYPT.getDefaultValue()); } - /** - * Beginning in version 6.0 of the Microsoft JDBC Driver for SQL Server, a new connection property transparentNetworkIPResolution (TNIR) is added - * for transparent connection to Always On availability groups or to a server which has multiple IP addresses associated. When - * transparentNetworkIPResolution is true, the driver attempts to connect to the first IP address available. If the first attempt fails, the - * driver tries to connect to all IP addresses in parallel until the timeout expires, discarding any pending connection attempts when one of them - * succeeds. - *

- * transparentNetworkIPResolution is ignored if multiSubnetFailover is true - *

- * transparentNetworkIPResolution is ignored if database mirroring is used - *

- * transparentNetworkIPResolution is ignored if there are more than 64 IP addresses - * - * @param tnir - * if set to true, the driver attempts to connect to the first IP address available. It is true by default. - */ + @Override public void setTransparentNetworkIPResolution(boolean tnir) { setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.TRANSPARENT_NETWORK_IP_RESOLUTION.toString(), tnir); } - /** - * Retrieves the TransparentNetworkIPResolution value. - * - * @return if enabled, returns true. Otherwise, false. - */ + @Override public boolean getTransparentNetworkIPResolution() { return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.TRANSPARENT_NETWORK_IP_RESOLUTION.toString(), SQLServerDriverBooleanProperty.TRANSPARENT_NETWORK_IP_RESOLUTION.getDefaultValue()); } + @Override public void setTrustServerCertificate(boolean e) { setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.TRUST_SERVER_CERTIFICATE.toString(), e); } + @Override public boolean getTrustServerCertificate() { return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.TRUST_SERVER_CERTIFICATE.toString(), SQLServerDriverBooleanProperty.TRUST_SERVER_CERTIFICATE.getDefaultValue()); } + @Override public void setTrustStoreType(String trustStoreType) { setStringProperty(connectionProps, SQLServerDriverStringProperty.TRUST_STORE_TYPE.toString(), trustStoreType); } + @Override public String getTrustStoreType() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.TRUST_STORE_TYPE.toString(), SQLServerDriverStringProperty.TRUST_STORE_TYPE.getDefaultValue()); } - public void setTrustStore(String st) { - setStringProperty(connectionProps, SQLServerDriverStringProperty.TRUST_STORE.toString(), st); + @Override + public void setTrustStore(String trustStore) { + setStringProperty(connectionProps, SQLServerDriverStringProperty.TRUST_STORE.toString(), trustStore); } + @Override public String getTrustStore() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.TRUST_STORE.toString(), null); } - public void setTrustStorePassword(String p) { + @Override + public void setTrustStorePassword(String trustStorePassword) { // if a non value property is set - if (p != null) + if (trustStorePassword != null) trustStorePasswordStripped = false; - setStringProperty(connectionProps, SQLServerDriverStringProperty.TRUST_STORE_PASSWORD.toString(), p); + setStringProperty(connectionProps, SQLServerDriverStringProperty.TRUST_STORE_PASSWORD.toString(), trustStorePassword); } - public void setHostNameInCertificate(String host) { - setStringProperty(connectionProps, SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString(), host); + @Override + public void setHostNameInCertificate(String hostName) { + setStringProperty(connectionProps, SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString(), hostName); } + @Override public String getHostNameInCertificate() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString(), null); } - // lockTimeout is the number of milliseconds to wait before the database reports + // lockTimeout is the number of milliseconds to wait before the database + // reports // a lock timeout. The default value of -1 means wait forever. If specified, - // this value will be the default for all statements on the connection. Note a - // value of 0 means no wait. If lockTimeout is not set, getLockTimeout returns + // this value will be the default for all statements on the connection. Note + // a + // value of 0 means no wait. If lockTimeout is not set, getLockTimeout + // returns // the default of -1. + @Override public void setLockTimeout(int lockTimeout) { setIntProperty(connectionProps, SQLServerDriverIntProperty.LOCK_TIMEOUT.toString(), lockTimeout); } + @Override public int getLockTimeout() { return getIntProperty(connectionProps, SQLServerDriverIntProperty.LOCK_TIMEOUT.toString(), SQLServerDriverIntProperty.LOCK_TIMEOUT.getDefaultValue()); } - // setPassword sets the password that will be used when connecting to SQL Server. - // Note getPassword is deliberately declared non-public for security reasons. - // If the password is not set, getPassword returns the default value of null. + // setPassword sets the password that will be used when connecting to SQL + // Server. + // Note getPassword is deliberately declared non-public for security + // reasons. + // If the password is not set, getPassword returns the default value of + // null. + @Override public void setPassword(String password) { setStringProperty(connectionProps, SQLServerDriverStringProperty.PASSWORD.toString(), password); } @@ -404,15 +380,19 @@ String getPassword() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.PASSWORD.toString(), null); } - // portNumber is the TCP-IP port number used when opening a socket connection - // to SQL Server. If portNumber is not set, getPortNumber returns the default + // portNumber is the TCP-IP port number used when opening a socket + // connection + // to SQL Server. If portNumber is not set, getPortNumber returns the + // default // of 1433. Note as mentioned above, setPortNumber does not do any range // checking on the port value passed in, invalid port numbers like 99999 can // be passed in without triggering any error. + @Override public void setPortNumber(int portNumber) { setIntProperty(connectionProps, SQLServerDriverIntProperty.PORT_NUMBER.toString(), portNumber); } + @Override public int getPortNumber() { return getIntProperty(connectionProps, SQLServerDriverIntProperty.PORT_NUMBER.toString(), SQLServerDriverIntProperty.PORT_NUMBER.getDefaultValue()); @@ -420,143 +400,167 @@ public int getPortNumber() { // selectMethod is the default cursor type used for the result set. This // property is useful when you are dealing with large result sets and don't - // want to store the whole result set in memory on the client side. By setting - // the property to "cursor" you will be able to create a server side cursor that + // want to store the whole result set in memory on the client side. By + // setting + // the property to "cursor" you will be able to create a server side cursor + // that // can fetch smaller chunks of data at a time. If selectMethod is not set, // getSelectMethod returns the default value of "direct". + @Override public void setSelectMethod(String selectMethod) { setStringProperty(connectionProps, SQLServerDriverStringProperty.SELECT_METHOD.toString(), selectMethod); } + @Override public String getSelectMethod() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.SELECT_METHOD.toString(), SQLServerDriverStringProperty.SELECT_METHOD.getDefaultValue()); } - public void setResponseBuffering(String respo) { - setStringProperty(connectionProps, SQLServerDriverStringProperty.RESPONSE_BUFFERING.toString(), respo); + @Override + public void setResponseBuffering(String bufferingMode) { + setStringProperty(connectionProps, SQLServerDriverStringProperty.RESPONSE_BUFFERING.toString(), bufferingMode); } + @Override public String getResponseBuffering() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.RESPONSE_BUFFERING.toString(), SQLServerDriverStringProperty.RESPONSE_BUFFERING.getDefaultValue()); } + @Override public void setApplicationIntent(String applicationIntent) { setStringProperty(connectionProps, SQLServerDriverStringProperty.APPLICATION_INTENT.toString(), applicationIntent); } + @Override public String getApplicationIntent() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.APPLICATION_INTENT.toString(), SQLServerDriverStringProperty.APPLICATION_INTENT.getDefaultValue()); } + @Override public void setSendTimeAsDatetime(boolean sendTimeAsDatetime) { setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.SEND_TIME_AS_DATETIME.toString(), sendTimeAsDatetime); } + @Override public boolean getSendTimeAsDatetime() { return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.SEND_TIME_AS_DATETIME.toString(), SQLServerDriverBooleanProperty.SEND_TIME_AS_DATETIME.getDefaultValue()); } // If sendStringParametersAsUnicode is set to true (which is the default), - // string parameters are sent to the server in UNICODE format. If sendStringParametersAsUnicode - // is set to false, string parameters are sent to the server in the native TDS collation - // format of the database, not in UNICODE. If sendStringParametersAsUnicode is not set, + // string parameters are sent to the server in UNICODE format. If + // sendStringParametersAsUnicode + // is set to false, string parameters are sent to the server in the native + // TDS collation + // format of the database, not in UNICODE. If sendStringParametersAsUnicode + // is not set, // getSendStringParametersAsUnicode returns the default of true. + @Override public void setSendStringParametersAsUnicode(boolean sendStringParametersAsUnicode) { setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.SEND_STRING_PARAMETERS_AS_UNICODE.toString(), sendStringParametersAsUnicode); } + @Override public boolean getSendStringParametersAsUnicode() { return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.SEND_STRING_PARAMETERS_AS_UNICODE.toString(), SQLServerDriverBooleanProperty.SEND_STRING_PARAMETERS_AS_UNICODE.getDefaultValue()); } - /** - * Translates the serverName from Unicode to ASCII Compatible Encoding (ACE) - * - * @param serverNameAsACE - * if enabled the servername will be translated to ASCII Compatible Encoding (ACE) - */ + @Override public void setServerNameAsACE(boolean serverNameAsACE) { setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.SERVER_NAME_AS_ACE.toString(), serverNameAsACE); } - /** - * Retrieves if the serverName should be translated from Unicode to ASCII Compatible Encoding (ACE) - * - * @return if enabled, will return true. Otherwise, false. - */ + @Override public boolean getServerNameAsACE() { return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.SERVER_NAME_AS_ACE.toString(), SQLServerDriverBooleanProperty.SERVER_NAME_AS_ACE.getDefaultValue()); } - // serverName is the host name of the target SQL Server. If serverName is not set, + // serverName is the host name of the target SQL Server. If serverName is + // not set, // getServerName returns the default value of null is returned. + @Override public void setServerName(String serverName) { setStringProperty(connectionProps, SQLServerDriverStringProperty.SERVER_NAME.toString(), serverName); } + @Override public String getServerName() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.SERVER_NAME.toString(), null); } // Specify an Service Principal Name (SPN) of the target SQL Server. // https://msdn.microsoft.com/en-us/library/cc280459.aspx + @Override public void setServerSpn(String serverSpn) { setStringProperty(connectionProps, SQLServerDriverStringProperty.SERVER_SPN.toString(), serverSpn); } + @Override public String getServerSpn() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.SERVER_SPN.toString(), null); } - // serverName is the host name of the target SQL Server. If serverName is not set, + // serverName is the host name of the target SQL Server. If serverName is + // not set, // getServerName returns the default value of null is returned. + @Override public void setFailoverPartner(String serverName) { setStringProperty(connectionProps, SQLServerDriverStringProperty.FAILOVER_PARTNER.toString(), serverName); } + @Override public String getFailoverPartner() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.FAILOVER_PARTNER.toString(), null); } + @Override public void setMultiSubnetFailover(boolean multiSubnetFailover) { setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.MULTI_SUBNET_FAILOVER.toString(), multiSubnetFailover); } + @Override public boolean getMultiSubnetFailover() { return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.MULTI_SUBNET_FAILOVER.toString(), SQLServerDriverBooleanProperty.MULTI_SUBNET_FAILOVER.getDefaultValue()); } - // setUser set's the user name that will be used when connecting to SQL Server. + // setUser set's the user name that will be used when connecting to SQL + // Server. // If user is not set, getUser returns the default value of null. + @Override public void setUser(String user) { setStringProperty(connectionProps, SQLServerDriverStringProperty.USER.toString(), user); } + @Override public String getUser() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.USER.toString(), null); } // workstationID is the name of the client machine (or client workstation). - // workstationID is the host name of the client in other words. If workstationID - // is not set, the default value is constructed by calling InetAddress.getLocalHost().getHostName() + // workstationID is the host name of the client in other words. If + // workstationID + // is not set, the default value is constructed by calling + // InetAddress.getLocalHost().getHostName() // or if getHostName() returns blank then getHostAddress().toString(). + @Override public void setWorkstationID(String workstationID) { setStringProperty(connectionProps, SQLServerDriverStringProperty.WORKSTATION_ID.toString(), workstationID); } + @Override public String getWorkstationID() { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getWorkstationID"); String getWSID = connectionProps.getProperty(SQLServerDriverStringProperty.WORKSTATION_ID.toString()); - // Per spec, return what the logon will send here if workstationID property is not set. + // Per spec, return what the logon will send here if workstationID + // property is not set. if (null == getWSID) { getWSID = Util.lookupHostName(); } @@ -564,67 +568,89 @@ public String getWorkstationID() { return getWSID; } - // If xopenStates is set to true, the driver will convert SQL states to XOPEN - // compliant states. The default is false which causes the driver to generate SQL 99 - // state codes. If xopenStates is not set, getXopenStates returns the default value + // If xopenStates is set to true, the driver will convert SQL states to + // XOPEN + // compliant states. The default is false which causes the driver to + // generate SQL 99 + // state codes. If xopenStates is not set, getXopenStates returns the + // default value // of false. + @Override public void setXopenStates(boolean xopenStates) { setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.XOPEN_STATES.toString(), xopenStates); } + @Override public boolean getXopenStates() { return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.XOPEN_STATES.toString(), SQLServerDriverBooleanProperty.XOPEN_STATES.getDefaultValue()); } - + + @Override public void setFIPS(boolean fips) { setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.FIPS.toString(), fips); } + @Override public boolean getFIPS() { return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.FIPS.toString(), SQLServerDriverBooleanProperty.FIPS.getDefaultValue()); } - + + @Override public void setSSLProtocol(String sslProtocol) { setStringProperty(connectionProps, SQLServerDriverStringProperty.SSL_PROTOCOL.toString(), sslProtocol); } + @Override public String getSSLProtocol() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.SSL_PROTOCOL.toString(), SQLServerDriverStringProperty.SSL_PROTOCOL.getDefaultValue()); } + @Override public void setTrustManagerClass(String trustManagerClass) { setStringProperty(connectionProps, SQLServerDriverStringProperty.TRUST_MANAGER_CLASS.toString(), trustManagerClass); } + @Override public String getTrustManagerClass() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.TRUST_MANAGER_CLASS.toString(), SQLServerDriverStringProperty.TRUST_MANAGER_CLASS.getDefaultValue()); } - public void setTrustManagerConstructorArg(String trustManagerClass) { - setStringProperty(connectionProps, SQLServerDriverStringProperty.TRUST_MANAGER_CONSTRUCTOR_ARG.toString(), trustManagerClass); + @Override + public void setTrustManagerConstructorArg(String trustManagerConstructorArg) { + setStringProperty(connectionProps, SQLServerDriverStringProperty.TRUST_MANAGER_CONSTRUCTOR_ARG.toString(), trustManagerConstructorArg); } + @Override public String getTrustManagerConstructorArg() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.TRUST_MANAGER_CONSTRUCTOR_ARG.toString(), SQLServerDriverStringProperty.TRUST_MANAGER_CONSTRUCTOR_ARG.getDefaultValue()); } - // The URL property is exposed for backwards compatibility reasons. Also, several - // Java Application servers expect a setURL function on the DataSource and set it + // The URL property is exposed for backwards compatibility reasons. Also, + // several + // Java Application servers expect a setURL function on the DataSource and + // set it // by default (JBoss and WebLogic). - // Note for security reasons we do not recommend that customers include the password - // in the url supplied to setURL. The reason for this is third-party Java Application - // Servers will very often display the value set to URL property in their DataSource - // configuration GUI. We recommend instead that clients use the setPassword method - // to set the password value. The Java Application Servers will not display a password + // Note for security reasons we do not recommend that customers include the + // password + // in the url supplied to setURL. The reason for this is third-party Java + // Application + // Servers will very often display the value set to URL property in their + // DataSource + // configuration GUI. We recommend instead that clients use the setPassword + // method + // to set the password value. The Java Application Servers will not display + // a password // that is set on the DataSource in the configuration GUI. - // Note if setURL is not called, getURL returns the default value of "jdbc:sqlserver://". + // Note if setURL is not called, getURL returns the default value of + // "jdbc:sqlserver://". + @Override public void setURL(String url) { loggerExternal.entering(getClassNameLogging(), "setURL", url); // URL is not stored in a property set, it is maintained separately. @@ -632,6 +658,7 @@ public void setURL(String url) { loggerExternal.exiting(getClassNameLogging(), "setURL"); } + @Override public String getURL() { String url = dataSourceURL; loggerExternal.entering(getClassNameLogging(), "getURL"); @@ -643,15 +670,16 @@ public String getURL() { } // DataSource specific property setters/getters. - // Per JDBC specification 16.1.1 "...the only property that all DataSource // implementations are required to support is the description property". + @Override public void setDescription(String description) { loggerExternal.entering(getClassNameLogging(), "setDescription", description); dataSourceDescription = description; loggerExternal.exiting(getClassNameLogging(), "setDescription"); } + @Override public String getDescription() { loggerExternal.entering(getClassNameLogging(), "getDescription"); loggerExternal.exiting(getClassNameLogging(), "getDescription", dataSourceDescription); @@ -662,208 +690,121 @@ public String getDescription() { // buffer. It is also the value used for the TDS packet size (SQL Server // Network Packet Size). Validity of the value is checked at connect time. // If no value is set for this property, its default value is 4KB. + @Override public void setPacketSize(int packetSize) { setIntProperty(connectionProps, SQLServerDriverIntProperty.PACKET_SIZE.toString(), packetSize); } + @Override public int getPacketSize() { return getIntProperty(connectionProps, SQLServerDriverIntProperty.PACKET_SIZE.toString(), SQLServerDriverIntProperty.PACKET_SIZE.getDefaultValue()); } - /** - * Setting the query timeout - * - * @param queryTimeout - * The number of seconds to wait before a timeout has occurred on a query. The default value is 0, which means infinite timeout. - */ + @Override public void setQueryTimeout(int queryTimeout) { setIntProperty(connectionProps, SQLServerDriverIntProperty.QUERY_TIMEOUT.toString(), queryTimeout); } - /** - * Getting the query timeout - * - * @return The number of seconds to wait before a timeout has occurred on a query. - */ + @Override public int getQueryTimeout() { return getIntProperty(connectionProps, SQLServerDriverIntProperty.QUERY_TIMEOUT.toString(), SQLServerDriverIntProperty.QUERY_TIMEOUT.getDefaultValue()); } - - /** - * Setting the cancel timeout - * - * @param cancelQueryTimeout - * The number of seconds to wait before we wait for the query timeout to happen. - */ + + @Override public void setCancelQueryTimeout(int cancelQueryTimeout) { setIntProperty(connectionProps, SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.toString(), cancelQueryTimeout); } - /** - * Getting the cancel timeout - * - * @return the number of seconds to wait before we wait for the query timeout to happen. - */ + @Override public int getCancelQueryTimeout() { return getIntProperty(connectionProps, SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.toString(), SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.getDefaultValue()); } - /** - * If this configuration is false the first execution of a prepared statement will call sp_executesql and not prepare - * a statement, once the second execution happens it will call sp_prepexec and actually setup a prepared statement handle. Following - * executions will call sp_execute. This relieves the need for sp_unprepare on prepared statement close if the statement is only - * executed once. - * - * @param enablePrepareOnFirstPreparedStatementCall - * Changes the setting per the description. - */ + @Override public void setEnablePrepareOnFirstPreparedStatementCall(boolean enablePrepareOnFirstPreparedStatementCall) { - setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT.toString(), enablePrepareOnFirstPreparedStatementCall); + setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT.toString(), + enablePrepareOnFirstPreparedStatementCall); } - /** - * If this configuration returns false the first execution of a prepared statement will call sp_executesql and not prepare a statement, once the - * second execution happens it will call sp_prepexec and actually setup a prepared statement handle. Following executions will call sp_execute. - * This relieves the need for sp_unprepare on prepared statement close if the statement is only executed once. - * - * @return Returns the current setting per the description. - */ + @Override public boolean getEnablePrepareOnFirstPreparedStatementCall() { boolean defaultValue = SQLServerDriverBooleanProperty.ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT.getDefaultValue(); return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT.toString(), defaultValue); } - /** - * This setting controls how many outstanding prepared statement discard actions (sp_unprepare) can be outstanding per connection before a call to - * clean-up the outstanding handles on the server is executed. If the setting is {@literal <=} 1 unprepare actions will be executed immedietely on - * prepared statement close. If it is set to {@literal >} 1 these calls will be batched together to avoid overhead of calling sp_unprepare too - * often. - * - * @param serverPreparedStatementDiscardThreshold - * Changes the setting per the description. - */ + @Override public void setServerPreparedStatementDiscardThreshold(int serverPreparedStatementDiscardThreshold) { - setIntProperty(connectionProps, SQLServerDriverIntProperty.SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD.toString(), serverPreparedStatementDiscardThreshold); + setIntProperty(connectionProps, SQLServerDriverIntProperty.SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD.toString(), + serverPreparedStatementDiscardThreshold); } - /** - * This setting controls how many outstanding prepared statement discard actions (sp_unprepare) can be outstanding per connection before a call to - * clean-up the outstanding handles on the server is executed. If the setting is {@literal <=} 1 unprepare actions will be executed immedietely on - * prepared statement close. If it is set to {@literal >} 1 these calls will be batched together to avoid overhead of calling sp_unprepare too - * often. - * - * @return Returns the current setting per the description. - */ + @Override public int getServerPreparedStatementDiscardThreshold() { int defaultSize = SQLServerDriverIntProperty.SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD.getDefaultValue(); return getIntProperty(connectionProps, SQLServerDriverIntProperty.SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD.toString(), defaultSize); } - /** - * Specifies the size of the prepared statement cache for this connection. A value less than 1 means no cache. - * - * @param statementPoolingCacheSize - * Changes the setting per the description. - */ + @Override public void setStatementPoolingCacheSize(int statementPoolingCacheSize) { setIntProperty(connectionProps, SQLServerDriverIntProperty.STATEMENT_POOLING_CACHE_SIZE.toString(), statementPoolingCacheSize); } - /** - * Returns the size of the prepared statement cache for this connection. A value less than 1 means no cache. - * - * @return Returns the current setting per the description. - */ + @Override public int getStatementPoolingCacheSize() { int defaultSize = SQLServerDriverIntProperty.STATEMENT_POOLING_CACHE_SIZE.getDefaultValue(); return getIntProperty(connectionProps, SQLServerDriverIntProperty.STATEMENT_POOLING_CACHE_SIZE.toString(), defaultSize); } - - /** - * Disable/enable statement pooling. - * @param disableStatementPooling true to disable statement pooling, false to enable it. - */ + + @Override public void setDisableStatementPooling(boolean disableStatementPooling) { - setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.DISABLE_STATEMENT_POOLING.toString(), disableStatementPooling); + setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.DISABLE_STATEMENT_POOLING.toString(), disableStatementPooling); } - - /** - * Determine whether statement pooling is disabled. - * @return true if statement pooling is disabled, false if it is enabled. - */ + + @Override public boolean getDisableStatementPooling() { boolean defaultValue = SQLServerDriverBooleanProperty.DISABLE_STATEMENT_POOLING.getDefaultValue(); - return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.DISABLE_STATEMENT_POOLING.toString(), - defaultValue); + return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.DISABLE_STATEMENT_POOLING.toString(), defaultValue); } - /** - * Setting the socket timeout - * - * @param socketTimeout - * The number of milliseconds to wait before a timeout is occurred on a socket read or accept. The default value is 0, which means - * infinite timeout. - */ + @Override public void setSocketTimeout(int socketTimeout) { setIntProperty(connectionProps, SQLServerDriverIntProperty.SOCKET_TIMEOUT.toString(), socketTimeout); } - /** - * Getting the socket timeout - * - * @return The number of milliseconds to wait before a timeout is occurred on a socket read or accept. - */ + @Override public int getSocketTimeout() { int defaultTimeOut = SQLServerDriverIntProperty.SOCKET_TIMEOUT.getDefaultValue(); return getIntProperty(connectionProps, SQLServerDriverIntProperty.SOCKET_TIMEOUT.toString(), defaultTimeOut); } - - /** - * Setting the use Bulk Copy API for Batch Insert - * - * @param useBulkCopyForBatchInsert indicates whether Bulk Copy API should be used for Batch Insert operations. - */ + + @Override public void setUseBulkCopyForBatchInsert(boolean useBulkCopyForBatchInsert) { - setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT.toString(), - useBulkCopyForBatchInsert); + setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT.toString(), useBulkCopyForBatchInsert); } - /** - * Getting the use Bulk Copy API for Batch Insert - * - * @return whether the driver should use Bulk Copy API for Batch Insert operations. - */ + @Override public boolean getUseBulkCopyForBatchInsert() { return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT.toString(), SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT.getDefaultValue()); } - /** - * Sets the login configuration file for Kerberos authentication. This - * overrides the default configuration SQLJDBCDriver - * - * @param configurationName the configuration name - */ + @Override public void setJASSConfigurationName(String configurationName) { - setStringProperty(connectionProps, SQLServerDriverStringProperty.JAAS_CONFIG_NAME.toString(), - configurationName); + setStringProperty(connectionProps, SQLServerDriverStringProperty.JAAS_CONFIG_NAME.toString(), configurationName); } - /** - * Retrieves the login configuration file for Kerberos authentication. - * - * @return login configuration file name - */ + @Override public String getJASSConfigurationName() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.JAAS_CONFIG_NAME.toString(), SQLServerDriverStringProperty.JAAS_CONFIG_NAME.getDefaultValue()); } - - // responseBuffering controls the driver's buffering of responses from SQL Server. + + // responseBuffering controls the driver's buffering of responses from SQL + // Server. // Possible values are: // // "full" - Fully buffer the response at execution time. @@ -885,7 +826,8 @@ public String getJASSConfigurationName() { // Set a string property value. // Caller will always supply a non-null props and propKey. - // Caller may supply a null propValue, in this case no property value is set. + // Caller may supply a null propValue, in this case no property value is + // set. private void setStringProperty(Properties props, String propKey, String propValue) { @@ -1007,21 +949,25 @@ private Object getObjectProperty(Properties props, loggerExternal.exiting(getClassNameLogging(), "get" + propKey); return propValue; } - - // Returns a SQLServerConnection given username, password, and pooledConnection. - // Note that the DataSource properties set to connectionProps are used when creating + + // Returns a SQLServerConnection given username, password, and + // pooledConnection. + // Note that the DataSource properties set to connectionProps are used when + // creating // the connection. // Both username and password can be null. - // If pooledConnection is not null, then connection returned is attached to the pooledConnection + // If pooledConnection is not null, then connection returned is attached to + // the pooledConnection // and participates in connection pooling. SQLServerConnection getConnectionInternal(String username, String password, SQLServerPooledConnection pooledConnection) throws SQLServerException { Properties userSuppliedProps; Properties mergedProps; - // Trust store password stripped and this object got created via Objectfactory referencing. + // Trust store password stripped and this object got created via + // Objectfactory referencing. if (trustStorePasswordStripped) SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_referencingFailedTSP"), null, true); @@ -1048,7 +994,8 @@ SQLServerConnection getConnectionInternal(String username, // Merge in URL properties into userSuppliedProps if URL is set. if (null != dataSourceURL) { Properties urlProps = Util.parseUrl(dataSourceURL, dsLogger); - // null returned properties means that the passed in URL is not supported. + // null returned properties means that the passed in URL is not + // supported. if (null == urlProps) SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_errorConnectionString"), null, true); // Manually merge URL props and user supplied props. @@ -1076,6 +1023,7 @@ SQLServerConnection getConnectionInternal(String username, // Implement javax.naming.Referenceable interface methods. + @Override public Reference getReference() { loggerExternal.entering(getClassNameLogging(), "getReference"); Reference ref = getReferenceInternal("com.microsoft.sqlserver.jdbc.SQLServerDataSource"); @@ -1098,20 +1046,24 @@ Reference getReferenceInternal(String dataSourceClassString) { Enumeration e = connectionProps.keys(); while (e.hasMoreElements()) { String propertyName = (String) e.nextElement(); - // If a trustStore password is set, it is omitted and a trustStorePasswordSet flag is set. + // If a trustStore password is set, it is omitted and a + // trustStorePasswordSet flag is set. if (propertyName.equals(SQLServerDriverStringProperty.TRUST_STORE_PASSWORD.toString())) { - // The property set and the variable set at the same time is not possible + // The property set and the variable set at the same time is not + // possible assert trustStorePasswordStripped == false; ref.add(new StringRefAddr("trustStorePasswordStripped", "true")); } else { - // do not add passwords to the collection. we have normal password + // do not add passwords to the collection. we have normal + // password if (!propertyName.contains(SQLServerDriverStringProperty.PASSWORD.toString())) ref.add(new StringRefAddr(propertyName, connectionProps.getProperty(propertyName))); } } - // Add dataSourceURL and dataSourceDescription as these will not appear in connectionProps. + // Add dataSourceURL and dataSourceDescription as these will not appear + // in connectionProps. if (null != dataSourceURL) ref.add(new StringRefAddr("dataSourceURL", dataSourceURL)); @@ -1121,10 +1073,13 @@ Reference getReferenceInternal(String dataSourceClassString) { return ref; } - // Initialize this datasource from properties found inside the reference ref. - // Called by SQLServerDataSourceObjectFactory to initialize new DataSource instance. + // Initialize this datasource from properties found inside the reference + // ref. + // Called by SQLServerDataSourceObjectFactory to initialize new DataSource + // instance. void initializeFromReference(javax.naming.Reference ref) { - // Enumerate all the StringRefAddr objects in the Reference and assign properties appropriately. + // Enumerate all the StringRefAddr objects in the Reference and assign + // properties appropriately. Enumeration e = ref.getAll(); while (e.hasMoreElements()) { StringRefAddr addr = (StringRefAddr) e.nextElement(); @@ -1141,7 +1096,8 @@ else if ("dataSourceDescription".equals(propertyName)) { else if ("trustStorePasswordStripped".equals(propertyName)) { trustStorePasswordStripped = true; } - // Just skip "class" StringRefAddr, it does not go into connectionProps + // Just skip "class" StringRefAddr, it does not go into + // connectionProps else if (!"class".equals(propertyName)) { connectionProps.setProperty(propertyName, propertyValue); @@ -1149,6 +1105,7 @@ else if (!"class".equals(propertyName)) { } } + @Override public boolean isWrapperFor(Class iface) throws SQLException { loggerExternal.entering(getClassNameLogging(), "isWrapperFor", iface); boolean f = iface.isInstance(this); @@ -1156,6 +1113,7 @@ public boolean isWrapperFor(Class iface) throws SQLException { return f; } + @Override public T unwrap(Class iface) throws SQLException { loggerExternal.entering(getClassNameLogging(), "unwrap", iface); T t; @@ -1169,7 +1127,6 @@ public T unwrap(Class iface) throws SQLException { return t; } - // Returns unique id for each DataSource instance. private static int nextDataSourceID() { return baseDataSourceID.incrementAndGet(); @@ -1180,8 +1137,10 @@ private Object writeReplace() throws java.io.ObjectStreamException { } private void readObject(java.io.ObjectInputStream stream) throws java.io.InvalidObjectException { - // For added security/robustness, the only way to rehydrate a serialized SQLServerDataSource - // is to use a SerializationProxy. Direct use of readObject() is not supported. + // For added security/robustness, the only way to rehydrate a serialized + // SQLServerDataSource + // is to use a SerializationProxy. Direct use of readObject() is not + // supported. throw new java.io.InvalidObjectException(""); } @@ -1191,7 +1150,8 @@ private static class SerializationProxy implements java.io.Serializable { private static final long serialVersionUID = 654661379542314226L; SerializationProxy(SQLServerDataSource ds) { - // We do not need the class name so pass null, serialization mechanism + // We do not need the class name so pass null, serialization + // mechanism // stores the class info. ref = ds.getReferenceInternal(null); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java index 6518d7057..256635954 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java @@ -45,13 +45,14 @@ public Object getObjectInstance(Object ref, // Our reference will always have a "class" RefAddr. if (null == ra) { - SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true); + throwInvalidDataSourceRefException(); } String className = (String) ra.getContent(); - if (null == className) - SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true); + if (null == className) { + throwInvalidDataSourceRefException(); + } // Check that we have the expected class name inside our reference. if (("com.microsoft.sqlserver.jdbc.SQLServerDataSource").equals(className) @@ -69,32 +70,18 @@ public Object getObjectInstance(Object ref, return dataSourceClassInstance; } // Class not found, throw invalid reference exception. - SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true); - } - catch (ClassNotFoundException e) { - SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true); - } - catch (InstantiationException e) { - SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true); - } - catch (IllegalAccessException e) { - SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true); + throwInvalidDataSourceRefException(); } - catch (IllegalArgumentException e) { - SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true); - } - catch (InvocationTargetException e) { - SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true); - } - catch (NoSuchMethodException e) { - SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true); - } - catch (SecurityException e) { - SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true); + catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException + | NoSuchMethodException | SecurityException e) { + throwInvalidDataSourceRefException(); } // no chance of getting here but to keep the compiler happy return null; + } + private void throwInvalidDataSourceRefException() throws SQLServerException { + SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true); } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataTable.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataTable.java index 60188841a..1e894bdf9 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataTable.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataTable.java @@ -97,7 +97,6 @@ public synchronized void addColumnMetadata(SQLServerDataColumn column) throws SQ columnMetadata.put(columnCount++, column); } - /** * Adds one row of data to the data table. * @@ -139,14 +138,20 @@ public synchronized void addRow(Object... values) throws SQLServerException { } } - + /** * Adding rows one row of data to data table. - * @param jdbcType The jdbcType - * @param val The data value - * @param rowValues Row of data - * @param pair pair to be added to data table + * + * @param jdbcType + * The jdbcType + * @param val + * The data value + * @param rowValues + * Row of data + * @param pair + * pair to be added to data table * @throws SQLServerException + * when an error occurs */ private void internalAddrow(JDBCType jdbcType, Object val, @@ -218,7 +223,6 @@ private void internalAddrow(JDBCType jdbcType, case TIMESTAMP_WITH_TIMEZONE: case TIME_WITH_TIMEZONE: - DriverJDBCVersion.checkSupportsJDBC42(); case DATE: case TIME: case TIMESTAMP: @@ -290,17 +294,29 @@ else if (val instanceof OffsetTime) throw new SQLServerException(null, form.format(msgArgs), null, 0, false); } } - + + /** + * Retrieves java.util.Map object type of columnMetaData for all columns where column indexes are mapped with their + * respective {@link SQLServerDataColumn} Java object + * + * @return Map + */ public synchronized Map getColumnMetadata() { return columnMetadata; } + /** + * Returns name of TVP type set by {@link #setTvpName(String)} + * + * @return tvpName + */ public String getTvpName() { return tvpName; } /** - * Retrieves the column meta data of this data table. + * Sets the TVP Name + * * @param tvpName * the name of TVP */ diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 71729ff8b..9c9fb5513 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -38,7 +38,15 @@ public final class SQLServerDatabaseMetaData implements java.sql.DatabaseMetaDat static final private java.util.logging.Logger loggerExternal = java.util.logging.Logger .getLogger("com.microsoft.sqlserver.jdbc.internals.DatabaseMetaData"); - static private final AtomicInteger baseID = new AtomicInteger(0); // Unique id generator for each instance (used for logging). + static private final AtomicInteger baseID = new AtomicInteger(0); // Unique + // id + // generator + // for + // each + // instance + // (used + // for + // logging). final private String traceID; @@ -47,18 +55,17 @@ public final class SQLServerDatabaseMetaData implements java.sql.DatabaseMetaDat // uniqueidentifier https://msdn.microsoft.com/en-us/library/ms187942.aspx static final int uniqueidentifierSize = 36; - enum CallableHandles - { - SP_COLUMNS ("{ call sp_columns(?, ?, ?, ?, ?) }", "{ call sp_columns_100(?, ?, ?, ?, ?, ?) }"), - SP_COLUMN_PRIVILEGES ("{ call sp_column_privileges(?, ?, ?, ?)}", "{ call sp_column_privileges(?, ?, ?, ?)}"), - SP_TABLES ("{ call sp_tables(?, ?, ?, ?) }", "{ call sp_tables(?, ?, ?, ?) }"), - SP_SPECIAL_COLUMNS ("{ call sp_special_columns (?, ?, ?, ?, ?, ?, ?)}","{ call sp_special_columns_100 (?, ?, ?, ?, ?, ?, ?)}"), - SP_FKEYS ("{ call sp_fkeys (?, ?, ?, ? , ? ,?)}", "{ call sp_fkeys (?, ?, ?, ? , ? ,?)}"), - SP_STATISTICS ("{ call sp_statistics(?,?,?,?,?, ?) }", "{ call sp_statistics_100(?,?,?,?,?, ?) }"), - SP_SPROC_COLUMNS ("{ call sp_sproc_columns(?, ?, ?,?,?) }", "{ call sp_sproc_columns_100(?, ?, ?,?,?) }"), - SP_STORED_PROCEDURES ("{call sp_stored_procedures(?, ?, ?) }", "{call sp_stored_procedures(?, ?, ?) }"), - SP_TABLE_PRIVILEGES ("{call sp_table_privileges(?,?,?) }", "{call sp_table_privileges(?,?,?) }"), - SP_PKEYS ("{ call sp_pkeys (?, ?, ?)}", "{ call sp_pkeys (?, ?, ?)}"); + enum CallableHandles { + SP_COLUMNS("{ call sp_columns(?, ?, ?, ?, ?) }", "{ call sp_columns_100(?, ?, ?, ?, ?, ?) }"), + SP_COLUMN_PRIVILEGES("{ call sp_column_privileges(?, ?, ?, ?)}", "{ call sp_column_privileges(?, ?, ?, ?)}"), + SP_TABLES("{ call sp_tables(?, ?, ?, ?) }", "{ call sp_tables(?, ?, ?, ?) }"), + SP_SPECIAL_COLUMNS("{ call sp_special_columns (?, ?, ?, ?, ?, ?, ?)}", "{ call sp_special_columns_100 (?, ?, ?, ?, ?, ?, ?)}"), + SP_FKEYS("{ call sp_fkeys (?, ?, ?, ? , ? ,?)}", "{ call sp_fkeys (?, ?, ?, ? , ? ,?)}"), + SP_STATISTICS("{ call sp_statistics(?,?,?,?,?, ?) }", "{ call sp_statistics_100(?,?,?,?,?, ?) }"), + SP_SPROC_COLUMNS("{ call sp_sproc_columns(?, ?, ?,?,?) }", "{ call sp_sproc_columns_100(?, ?, ?,?,?) }"), + SP_STORED_PROCEDURES("{call sp_stored_procedures(?, ?, ?) }", "{call sp_stored_procedures(?, ?, ?) }"), + SP_TABLE_PRIVILEGES("{call sp_table_privileges(?,?,?) }", "{call sp_table_privileges(?,?,?) }"), + SP_PKEYS("{ call sp_pkeys (?, ?, ?)}", "{ call sp_pkeys (?, ?, ?)}"); // stored procs before Katmai ie SS10 private final String preKatProc; // procs on or after katmai @@ -112,7 +119,7 @@ final public String toString() { * @param con * the connection */ - /* L0 */ public SQLServerDatabaseMetaData(SQLServerConnection con) { + public SQLServerDatabaseMetaData(SQLServerConnection con) { traceID = " SQLServerDatabaseMetaData:" + nextInstanceID(); connection = con; if (logger.isLoggable(java.util.logging.Level.FINE)) { @@ -120,11 +127,13 @@ final public String toString() { } } + @Override public boolean isWrapperFor(Class iface) throws SQLException { boolean f = iface.isInstance(this); return f; } + @Override public T unwrap(Class iface) throws SQLException { T t; try { @@ -236,7 +245,7 @@ private void checkClosed() throws SQLServerException { * @param query * to execute * @return Resultset from the execution - * @throws SQLTimeoutException + * @throws SQLTimeoutException */ private SQLServerResultSet getResultSetFromInternalQueries(String catalog, String query) throws SQLServerException, SQLTimeoutException { @@ -283,7 +292,7 @@ private CallableStatement getCallableStatementHandle(CallableHandles request, * @param arguments * for the stored procedure * @return Resultset from the execution - * @throws SQLTimeoutException + * @throws SQLTimeoutException */ private SQLServerResultSet getResultSetFromStoredProc(String catalog, CallableHandles procedure, @@ -331,7 +340,7 @@ private SQLServerResultSet getResultSetWithProvidedColumnNames(String catalog, * @throws SQLServerException * @return the old catalog */ - /* L0 */ private String switchCatalogs(String catalog) throws SQLServerException { + private String switchCatalogs(String catalog) throws SQLServerException { if (catalog == null) return null; String sCurr = null; @@ -347,80 +356,92 @@ private SQLServerResultSet getResultSetWithProvidedColumnNames(String catalog, /* -------------- JDBC Interface API starts here ---------------- */ - /* L0 */ public boolean allProceduresAreCallable() throws SQLServerException { + @Override + public boolean allProceduresAreCallable() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean allTablesAreSelectable() throws SQLServerException { + @Override + public boolean allTablesAreSelectable() throws SQLServerException { checkClosed(); return true; } + @Override public boolean autoCommitFailureClosesAllResultSets() throws SQLException { checkClosed(); return false; } - /* L0 */ public boolean dataDefinitionCausesTransactionCommit() throws SQLServerException { + @Override + public boolean dataDefinitionCausesTransactionCommit() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean dataDefinitionIgnoredInTransactions() throws SQLServerException { + @Override + public boolean dataDefinitionIgnoredInTransactions() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean doesMaxRowSizeIncludeBlobs() throws SQLServerException { + @Override + public boolean doesMaxRowSizeIncludeBlobs() throws SQLServerException { checkClosed(); return false; } + @Override public boolean generatedKeyAlwaysReturned() throws SQLException { checkClosed(); - // driver supports retrieving generated keys return true; } + @Override public long getMaxLogicalLobSize() throws SQLException { - DriverJDBCVersion.checkSupportsJDBC42(); checkClosed(); - return MAXLOBSIZE; } + @Override public boolean supportsRefCursors() throws SQLException { - DriverJDBCVersion.checkSupportsJDBC42(); checkClosed(); - return false; } + @Override public boolean supportsSharding() throws SQLException { DriverJDBCVersion.checkSupportsJDBC43(); checkClosed(); - return false; } - /* L0 */ public java.sql.ResultSet getCatalogs() throws SQLServerException, SQLTimeoutException { + @Override + public java.sql.ResultSet getCatalogs() throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); } checkClosed(); // Return the orginal case instead of CAPS.removed Upper(). - String s = "SELECT name AS TABLE_CAT FROM sys.databases order by name"; // Need to match case of connection.getCatalog + String s = "SELECT name AS TABLE_CAT FROM sys.databases order by name"; // Need + // to + // match + // case + // of + // connection.getCatalog return getResultSetFromInternalQueries(null, s); } - /* L0 */ public String getCatalogSeparator() throws SQLServerException { + @Override + public String getCatalogSeparator() throws SQLServerException { checkClosed(); return "."; } - /* L0 */ public String getCatalogTerm() throws SQLServerException { + @Override + public String getCatalogTerm() throws SQLServerException { checkClosed(); return "database"; } @@ -428,7 +449,8 @@ public boolean supportsSharding() throws SQLException { private static final String[] getColumnPrivilegesColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ COLUMN_NAME, /* 5 */ GRANTOR, /* 6 */ GRANTEE, /* 7 */ PRIVILEGE, /* 8 */ IS_GRANTABLE}; - /* L0 */ public java.sql.ResultSet getColumnPrivileges(String catalog, + @Override + public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String col) throws SQLServerException, SQLTimeoutException { @@ -454,7 +476,8 @@ public boolean supportsSharding() throws SQLException { private static final String[] getTablesColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ TABLE_TYPE, /* 5 */ REMARKS}; - /* L0 */ public java.sql.ResultSet getTables(String catalog, + @Override + public java.sql.ResultSet getTables(String catalog, String schema, String table, String types[]) throws SQLServerException, SQLTimeoutException { @@ -555,8 +578,10 @@ private static String EscapeIDName(String inID) throws SQLServerException { /* 5 */ DATA_TYPE, /* 6 */ TYPE_NAME, /* 7 */ COLUMN_SIZE, /* 8 */ BUFFER_LENGTH, /* 9 */ DECIMAL_DIGITS, /* 10 */ NUM_PREC_RADIX, /* 11 */ NULLABLE, /* 12 */ REMARKS, /* 13 */ COLUMN_DEF, /* 14 */ SQL_DATA_TYPE, /* 15 */ SQL_DATETIME_SUB, /* 16 */ CHAR_OCTET_LENGTH, /* 17 */ ORDINAL_POSITION, /* 18 */ IS_NULLABLE}; - // SQL10 columns not exahustive we only need to set until the one we want to change - // in this case we want to change SS_IS_IDENTITY 22nd column to IS_AUTOINCREMENT + // SQL10 columns not exahustive we only need to set until the one we want to + // change + // in this case we want to change SS_IS_IDENTITY 22nd column to + // IS_AUTOINCREMENT // to be inline with JDBC spec private static final String[] getColumnsColumnNamesKatmai = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ COLUMN_NAME, /* 5 */ DATA_TYPE, /* 6 */ TYPE_NAME, /* 7 */ COLUMN_SIZE, /* 8 */ BUFFER_LENGTH, /* 9 */ DECIMAL_DIGITS, /* 10 */ NUM_PREC_RADIX, @@ -564,7 +589,8 @@ private static String EscapeIDName(String inID) throws SQLServerException { /* 17 */ ORDINAL_POSITION, /* 18 */ IS_NULLABLE, /* 20 */ SS_IS_SPARSE, /* 20 */ SS_IS_COLUMN_SET, /* 21 */ IS_GENERATEDCOLUMN, /* 22 */ IS_AUTOINCREMENT}; - /* L0 */ public java.sql.ResultSet getColumns(String catalog, + @Override + public java.sql.ResultSet getColumns(String catalog, String schema, String table, String col) throws SQLServerException, SQLTimeoutException { @@ -592,7 +618,8 @@ private static String EscapeIDName(String inID) throws SQLServerException { arguments[2] = catalog; arguments[3] = column; if (connection.isKatmaiOrLater()) { - arguments[4] = "2"; // give information about everything including sparse columns + arguments[4] = "2"; // give information about everything including + // sparse columns arguments[5] = "3"; // odbc version } else @@ -619,6 +646,7 @@ private static String EscapeIDName(String inID) throws SQLServerException { private static final String[] getFunctionsColumnNames = {/* 1 */ FUNCTION_CAT, /* 2 */ FUNCTION_SCHEM, /* 3 */ FUNCTION_NAME, /* 4 */ NUM_INPUT_PARAMS, /* 5 */ NUM_OUTPUT_PARAMS, /* 6 */ NUM_RESULT_SETS, /* 7 */ REMARKS, /* 8 */ FUNCTION_TYPE}; + @Override public java.sql.ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) throws SQLException { @@ -647,6 +675,7 @@ public java.sql.ResultSet getFunctions(String catalog, /* 11 */ RADIX, /* 12 */ NULLABLE, /* 13 */ REMARKS, /* 14 */ COLUMN_DEF, /* 15 */ SQL_DATA_TYPE, /* 16 */ SQL_DATETIME_SUB, /* 17 */ CHAR_OCTET_LENGTH, /* 18 */ ORDINAL_POSITION, /* 19 */ IS_NULLABLE}; + @Override public java.sql.ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, @@ -690,6 +719,7 @@ public java.sql.ResultSet getFunctionColumns(String catalog, return rs; } + @Override public java.sql.ResultSet getClientInfoProperties() throws SQLException { checkClosed(); return getResultSetFromInternalQueries(null, "SELECT" + @@ -702,7 +732,8 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { private static final String[] getBestRowIdentifierColumnNames = {/* 1 */ SCOPE, /* 2 */ COLUMN_NAME, /* 3 */ DATA_TYPE, /* 4 */ TYPE_NAME, /* 5 */ COLUMN_SIZE, /* 6 */ BUFFER_LENGTH, /* 7 */ DECIMAL_DIGITS, /* 8 */ PSEUDO_COLUMN}; - /* L0 */ public java.sql.ResultSet getBestRowIdentifier(String catalog, + @Override + public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, @@ -739,7 +770,8 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { return rs; } - /* L0 */ public java.sql.ResultSet getCrossReference(String cat1, + @Override + public java.sql.ResultSet getCrossReference(String cat1, String schem1, String tab1, String cat2, @@ -758,35 +790,42 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { return executeSPFkeys(arguments); } - /* L0 */ public String getDatabaseProductName() throws SQLServerException { + @Override + public String getDatabaseProductName() throws SQLServerException { checkClosed(); return "Microsoft SQL Server"; } - /* L0 */ public String getDatabaseProductVersion() throws SQLServerException { + @Override + public String getDatabaseProductVersion() throws SQLServerException { checkClosed(); return connection.sqlServerVersion; } - /* L0 */ public int getDefaultTransactionIsolation() throws SQLServerException { + @Override + public int getDefaultTransactionIsolation() throws SQLServerException { checkClosed(); return java.sql.Connection.TRANSACTION_READ_COMMITTED; } - /* L0 */ public int getDriverMajorVersion() { + @Override + public int getDriverMajorVersion() { return SQLJdbcVersion.major; } - /* L0 */ public int getDriverMinorVersion() { + @Override + public int getDriverMinorVersion() { return SQLJdbcVersion.minor; } - /* L0 */ public String getDriverName() throws SQLServerException { + @Override + public String getDriverName() throws SQLServerException { checkClosed(); return SQLServerDriver.PRODUCT_NAME; } - /* L0 */ public String getDriverVersion() throws SQLServerException { + @Override + public String getDriverVersion() throws SQLServerException { // driver version in the Major.Minor.MMDD.Revision form int n = getDriverMinorVersion(); @@ -799,80 +838,70 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { return s; } - /* L0 */ public java.sql.ResultSet getExportedKeys(String cat, + @Override + public java.sql.ResultSet getExportedKeys(String cat, String schema, String table) throws SQLServerException, SQLTimeoutException { return getCrossReference(cat, schema, table, null, null, null); } - /* L0 */ public String getExtraNameCharacters() throws SQLServerException { + @Override + public String getExtraNameCharacters() throws SQLServerException { checkClosed(); return "$#@"; } - /* L0 */ public String getIdentifierQuoteString() throws SQLServerException { + @Override + public String getIdentifierQuoteString() throws SQLServerException { checkClosed(); return "\""; } - /* L0 */ public java.sql.ResultSet getImportedKeys(String cat, + @Override + public java.sql.ResultSet getImportedKeys(String cat, String schema, String table) throws SQLServerException, SQLTimeoutException { return getCrossReference(null, null, null, cat, schema, table); } - private ResultSet executeSPFkeys(String[] procParams) throws SQLServerException, SQLTimeoutException - { + private ResultSet executeSPFkeys(String[] procParams) throws SQLServerException, SQLTimeoutException { String tempTableName = "@jdbc_temp_fkeys_result"; - String sql = "DECLARE " + tempTableName + " table (PKTABLE_QUALIFIER sysname, " + - "PKTABLE_OWNER sysname, " + - "PKTABLE_NAME sysname, " + - "PKCOLUMN_NAME sysname, " + - "FKTABLE_QUALIFIER sysname, " + - "FKTABLE_OWNER sysname, " + - "FKTABLE_NAME sysname, " + - "FKCOLUMN_NAME sysname, " + - "KEY_SEQ smallint, " + - "UPDATE_RULE smallint, " + - "DELETE_RULE smallint, " + - "FK_NAME sysname, " + - "PK_NAME sysname, " + - "DEFERRABILITY smallint);" + - "INSERT INTO " + tempTableName + " EXEC sp_fkeys ?,?,?,?,?,?;" + - "SELECT t.PKTABLE_QUALIFIER AS PKTABLE_CAT, " + - "t.PKTABLE_OWNER AS PKTABLE_SCHEM, " + - "t.PKTABLE_NAME, " + - "t.PKCOLUMN_NAME, " + - "t.FKTABLE_QUALIFIER AS FKTABLE_CAT, " + - "t.FKTABLE_OWNER AS FKTABLE_SCHEM, " + - "t.FKTABLE_NAME, " + - "t.FKCOLUMN_NAME, " + - "t.KEY_SEQ, " + - "CASE s.update_referential_action " + - "WHEN 1 THEN 0 " + //cascade - note that sp_fkey and sys.foreign_keys have flipped values for cascade and no action - "WHEN 0 THEN 3 " + //no action - "WHEN 2 THEN 2 " + //set null - "WHEN 3 THEN 4 " + //set default - "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;"; + String sql = "DECLARE " + tempTableName + " table (PKTABLE_QUALIFIER sysname, " + "PKTABLE_OWNER sysname, " + "PKTABLE_NAME sysname, " + + "PKCOLUMN_NAME sysname, " + "FKTABLE_QUALIFIER sysname, " + "FKTABLE_OWNER sysname, " + "FKTABLE_NAME sysname, " + + "FKCOLUMN_NAME sysname, " + "KEY_SEQ smallint, " + "UPDATE_RULE smallint, " + "DELETE_RULE smallint, " + "FK_NAME sysname, " + + "PK_NAME sysname, " + "DEFERRABILITY smallint);" + "INSERT INTO " + tempTableName + " EXEC sp_fkeys ?,?,?,?,?,?;" + + "SELECT t.PKTABLE_QUALIFIER AS PKTABLE_CAT, " + "t.PKTABLE_OWNER AS PKTABLE_SCHEM, " + "t.PKTABLE_NAME, " + "t.PKCOLUMN_NAME, " + + "t.FKTABLE_QUALIFIER AS FKTABLE_CAT, " + "t.FKTABLE_OWNER AS FKTABLE_SCHEM, " + "t.FKTABLE_NAME, " + "t.FKCOLUMN_NAME, " + + "t.KEY_SEQ, " + "CASE s.update_referential_action " + "WHEN 1 THEN 0 " + // cascade + // - + // note + // that + // sp_fkey + // and + // sys.foreign_keys + // have + // flipped + // values + // for + // cascade + // and + // no + // action + "WHEN 0 THEN 3 " + // no action + "WHEN 2 THEN 2 " + // set null + "WHEN 3 THEN 4 " + // set default + "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;"; SQLServerCallableStatement cstmt = (SQLServerCallableStatement) connection.prepareCall(sql); for (int i = 0; i < 6; i++) { - cstmt.setString(i+1, procParams[i]); + cstmt.setString(i + 1, procParams[i]); } String currentDB = null; - if (procParams[2] != null && procParams[2] != "") {//pktable_qualifier + if (procParams[2] != null && procParams[2] != "") {// pktable_qualifier currentDB = switchCatalogs(procParams[2]); - } else if (procParams[5] != null && procParams[5] != "") {//fktable_qualifier + } + else if (procParams[5] != null && procParams[5] != "") {// fktable_qualifier currentDB = switchCatalogs(procParams[5]); } ResultSet rs = cstmt.executeQuery(); @@ -886,7 +915,8 @@ private ResultSet executeSPFkeys(String[] procParams) throws SQLServerException, /* 5 */ INDEX_QUALIFIER, /* 6 */ INDEX_NAME, /* 7 */ TYPE, /* 8 */ ORDINAL_POSITION, /* 9 */ COLUMN_NAME, /* 10 */ ASC_OR_DESC, /* 11 */ CARDINALITY, /* 12 */ PAGES, /* 13 */ FILTER_CONDITION}; - /* L0 */ public java.sql.ResultSet getIndexInfo(String cat, + @Override + public java.sql.ResultSet getIndexInfo(String cat, String schema, String table, boolean unique, @@ -916,52 +946,62 @@ private ResultSet executeSPFkeys(String[] procParams) throws SQLServerException, return getResultSetWithProvidedColumnNames(cat, CallableHandles.SP_STATISTICS, arguments, getIndexInfoColumnNames); } - /* L0 */ public int getMaxBinaryLiteralLength() throws SQLServerException { + @Override + public int getMaxBinaryLiteralLength() throws SQLServerException { checkClosed(); return 0; } - /* L0 */ public int getMaxCatalogNameLength() throws SQLServerException { + @Override + public int getMaxCatalogNameLength() throws SQLServerException { checkClosed(); return 128; } - /* L0 */ public int getMaxCharLiteralLength() throws SQLServerException { + @Override + public int getMaxCharLiteralLength() throws SQLServerException { checkClosed(); return 0; } - /* L0 */ public int getMaxColumnNameLength() throws SQLServerException { + @Override + public int getMaxColumnNameLength() throws SQLServerException { checkClosed(); return 128; } - /* L0 */ public int getMaxColumnsInGroupBy() throws SQLServerException { + @Override + public int getMaxColumnsInGroupBy() throws SQLServerException { checkClosed(); return 0; } - /* L0 */ public int getMaxColumnsInIndex() throws SQLServerException { + @Override + public int getMaxColumnsInIndex() throws SQLServerException { checkClosed(); return 16; } - /* L0 */ public int getMaxColumnsInOrderBy() throws SQLServerException { + @Override + public int getMaxColumnsInOrderBy() throws SQLServerException { checkClosed(); return 0; } - /* L0 */ public int getMaxColumnsInSelect() throws SQLServerException { + @Override + public int getMaxColumnsInSelect() throws SQLServerException { checkClosed(); return 4096; } - /* L0 */ public int getMaxColumnsInTable() throws SQLServerException { + @Override + public int getMaxColumnsInTable() throws SQLServerException { checkClosed(); return 1024; } - /* L0 */ public int getMaxConnections() throws SQLServerException, SQLTimeoutException { + @Override + public int getMaxConnections() throws SQLServerException, SQLTimeoutException { checkClosed(); try { String s = "sp_configure 'user connections'"; @@ -976,62 +1016,76 @@ private ResultSet executeSPFkeys(String[] procParams) throws SQLServerException, } - /* L0 */ public int getMaxCursorNameLength() throws SQLServerException { + @Override + public int getMaxCursorNameLength() throws SQLServerException { checkClosed(); return 0; } - /* L0 */ public int getMaxIndexLength() throws SQLServerException { + @Override + public int getMaxIndexLength() throws SQLServerException { checkClosed(); return 900; } - /* L0 */ public int getMaxProcedureNameLength() throws SQLServerException { + @Override + public int getMaxProcedureNameLength() throws SQLServerException { checkClosed(); return 128; } - /* L0 */ public int getMaxRowSize() throws SQLServerException { + @Override + public int getMaxRowSize() throws SQLServerException { checkClosed(); return 8060; } - /* L0 */ public int getMaxSchemaNameLength() throws SQLServerException { + @Override + public int getMaxSchemaNameLength() throws SQLServerException { checkClosed(); return 128; } - /* L0 */ public int getMaxStatementLength() throws SQLServerException { + @Override + public int getMaxStatementLength() throws SQLServerException { checkClosed(); - // SQL Server currently limits to 64K the number of TDS packets per conversation. - // This number multiplied by the size of each TDS packet yields the maximum total - // size of any request to the server, which is therefore an upper bound to the + // SQL Server currently limits to 64K the number of TDS packets per + // conversation. + // This number multiplied by the size of each TDS packet yields the + // maximum total + // size of any request to the server, which is therefore an upper bound + // to the // maximum SQL statement length. return 65536 * connection.getTDSPacketSize(); } - /* L0 */ public int getMaxStatements() throws SQLServerException { + @Override + public int getMaxStatements() throws SQLServerException { checkClosed(); return 0; } - /* L0 */ public int getMaxTableNameLength() throws SQLServerException { + @Override + public int getMaxTableNameLength() throws SQLServerException { checkClosed(); return 128; } - /* L0 */ public int getMaxTablesInSelect() throws SQLServerException { + @Override + public int getMaxTablesInSelect() throws SQLServerException { checkClosed(); return 256; } - /* L0 */ public int getMaxUserNameLength() throws SQLServerException { + @Override + public int getMaxUserNameLength() throws SQLServerException { checkClosed(); return 128; } - /* L0 */ public String getNumericFunctions() throws SQLServerException { + @Override + public String getNumericFunctions() throws SQLServerException { checkClosed(); return "ABS,ACOS,ASIN,ATAN,ATAN2,CEILING,COS,COT,DEGREES,EXP, FLOOR,LOG,LOG10,MOD,PI,POWER,RADIANS,RAND,ROUND,SIGN,SIN,SQRT,TAN,TRUNCATE"; } @@ -1039,7 +1093,8 @@ private ResultSet executeSPFkeys(String[] procParams) throws SQLServerException, private static final String[] getPrimaryKeysColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ COLUMN_NAME, /* 5 */ KEY_SEQ, /* 6 */ PK_NAME}; - /* L0 */ public java.sql.ResultSet getPrimaryKeys(String cat, + @Override + public java.sql.ResultSet getPrimaryKeys(String cat, String schema, String table) throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -1061,7 +1116,8 @@ private ResultSet executeSPFkeys(String[] procParams) throws SQLServerException, /* 11 */ RADIX, /* 12 */ NULLABLE, /* 13 */ REMARKS, /* 14 */ COLUMN_DEF, /* 15 */ SQL_DATA_TYPE, /* 16 */ SQL_DATETIME_SUB, /* 17 */ CHAR_OCTET_LENGTH, /* 18 */ ORDINAL_POSITION, /* 19 */ IS_NULLABLE}; - /* L0 */ public java.sql.ResultSet getProcedureColumns(String catalog, + @Override + public java.sql.ResultSet getProcedureColumns(String catalog, String schema, String proc, String col) throws SQLServerException, SQLTimeoutException { @@ -1104,7 +1160,8 @@ private ResultSet executeSPFkeys(String[] procParams) throws SQLServerException, private static final String[] getProceduresColumnNames = {/* 1 */ PROCEDURE_CAT, /* 2 */ PROCEDURE_SCHEM, /* 3 */ PROCEDURE_NAME, /* 4 */ NUM_INPUT_PARAMS, /* 5 */ NUM_OUTPUT_PARAMS, /* 6 */ NUM_RESULT_SETS, /* 7 */ REMARKS, /* 8 */ PROCEDURE_TYPE}; - /* L0 */ public java.sql.ResultSet getProcedures(String catalog, + @Override + public java.sql.ResultSet getProcedures(String catalog, String schema, String proc) throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -1123,11 +1180,13 @@ private ResultSet executeSPFkeys(String[] procParams) throws SQLServerException, return getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_STORED_PROCEDURES, arguments, getProceduresColumnNames); } - /* L0 */ public String getProcedureTerm() throws SQLServerException { + @Override + public String getProcedureTerm() throws SQLServerException { checkClosed(); return "stored procedure"; } + @Override public ResultSet getPseudoColumns(String catalog, String schemaPattern, String tableNamePattern, @@ -1139,7 +1198,8 @@ public ResultSet getPseudoColumns(String catalog, checkClosed(); // SQL server does not support pseudo columns for identifiers - // as per http://msdn.microsoft.com/en-us/library/ms378445%28v=sql.110%29.aspx + // as per + // http://msdn.microsoft.com/en-us/library/ms378445%28v=sql.110%29.aspx // so just return empty result set return getResultSetFromInternalQueries(catalog, "SELECT" + /* 1 */ " cast(NULL as char(1)) as TABLE_CAT," + @@ -1156,7 +1216,8 @@ public ResultSet getPseudoColumns(String catalog, /* 13 */ " cast(NULL as char(1)) as IS_NULLABLE" + " where 0 = 1"); } - /* L0 */ public java.sql.ResultSet getSchemas() throws SQLServerException, SQLTimeoutException { + @Override + public java.sql.ResultSet getSchemas() throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); } @@ -1169,7 +1230,8 @@ private java.sql.ResultSet getSchemasInternal(String catalog, String schemaPattern) throws SQLServerException, SQLTimeoutException { String s; - // The schemas that return null for catalog name, these are prebuilt schemas shipped by SQLServer, if SQLServer adds anymore of these + // The schemas that return null for catalog name, these are prebuilt + // schemas shipped by SQLServer, if SQLServer adds anymore of these // we need to add them here. String constSchemas = " ('dbo', 'guest','INFORMATION_SCHEMA','sys','db_owner', 'db_accessadmin', 'db_securityadmin', 'db_ddladmin' " + " ,'db_backupoperator','db_datareader','db_datawriter','db_denydatareader','db_denydatawriter') "; @@ -1182,9 +1244,12 @@ private java.sql.ResultSet getSchemasInternal(String catalog, schemaName = catalogId + "." + schemaName; } - // The common schemas need to be under null catalog name however the schemas specific to the particular catalog has to have the current + // The common schemas need to be under null catalog name however the + // schemas specific to the particular catalog has to have the current // catalog name - // to achive this, first we figure out the common schemas by intersecting current catalogs schemas with the const schemas (ie builtinSchemas) + // to achive this, first we figure out the common schemas by + // intersecting current catalogs schemas with the const schemas (ie + // builtinSchemas) s = "select " + schemaName + " 'TABLE_SCHEM',"; if (null != catalog && catalog.length() == 0) { s += "null 'TABLE_CATALOG' "; @@ -1225,7 +1290,8 @@ else if (null != schemaPattern) else { // The prepared statement is not closed after execution. - // Yes we will "leak a server handle" per execution but the connection closure will release them + // Yes we will "leak a server handle" per execution but the + // connection closure will release them // SQLServerPreparedStatement ps = (SQLServerPreparedStatement) connection.prepareStatement(s); ps.setString(1, schemaPattern); @@ -1234,6 +1300,7 @@ else if (null != schemaPattern) return rs; } + @Override public java.sql.ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -1242,35 +1309,42 @@ public java.sql.ResultSet getSchemas(String catalog, return getSchemasInternal(catalog, schemaPattern); } - /* L0 */ public String getSchemaTerm() throws SQLServerException { + @Override + public String getSchemaTerm() throws SQLServerException { checkClosed(); return "schema"; } - /* L0 */ public String getSearchStringEscape() throws SQLServerException { + @Override + public String getSearchStringEscape() throws SQLServerException { checkClosed(); return "\\"; } - /* L0 */ public String getSQLKeywords() throws SQLServerException { + @Override + public String getSQLKeywords() throws SQLServerException { checkClosed(); return "BACKUP,BREAK,BROWSE,BULK,CHECKPOINT,CLUSTERED,COMPUTE,CONTAINS,CONTAINSTABLE,DATABASE,DBCC,DENY,DISK,DISTRIBUTED,DUMMY,DUMP,ERRLVL,EXIT,FILE,FILLFACTOR,FREETEXT,FREETEXTTABLE,FUNCTION,HOLDLOCK,IDENTITY_INSERT,IDENTITYCOL,IF,KILL,LINENO,LOAD,NOCHECK,NONCLUSTERED,OFF,OFFSETS,OPENDATASOURCE,OPENQUERY,OPENROWSET,OPENXML,OVER,PERCENT,PLAN,PRINT,PROC,RAISERROR,READTEXT,RECONFIGURE,REPLICATION,RESTORE,RETURN,ROWCOUNT,ROWGUIDCOL,RULE,SAVE,SETUSER,SHUTDOWN,STATISTICS,TEXTSIZE,TOP,TRAN,TRIGGER,TRUNCATE,TSEQUAL,UPDATETEXT,USE,WAITFOR,WHILE,WRITETEXT"; } - /* L0 */ public String getStringFunctions() throws SQLServerException { + @Override + public String getStringFunctions() throws SQLServerException { checkClosed(); return "ASCII,CHAR,CONCAT, DIFFERENCE,INSERT,LCASE,LEFT,LENGTH,LOCATE,LTRIM,REPEAT,REPLACE,RIGHT,RTRIM,SOUNDEX,SPACE,SUBSTRING,UCASE"; } - /* L0 */ public String getSystemFunctions() throws SQLServerException { + @Override + public String getSystemFunctions() throws SQLServerException { checkClosed(); - return "DATABASE,IFNULL,USER"; // The functions no reinstated after the CTS certification. + return "DATABASE,IFNULL,USER"; // The functions no reinstated after the + // CTS certification. } private static final String[] getTablePrivilegesColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ GRANTOR, /* 5 */ GRANTEE, /* 6 */ PRIVILEGE, /* 7 */ IS_GRANTABLE}; - /* L0 */ public java.sql.ResultSet getTablePrivileges(String catalog, + @Override + public java.sql.ResultSet getTablePrivileges(String catalog, String schema, String table) throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -1291,7 +1365,8 @@ public java.sql.ResultSet getSchemas(String catalog, return getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_TABLE_PRIVILEGES, arguments, getTablePrivilegesColumnNames); } - /* L0 */ public java.sql.ResultSet getTableTypes() throws SQLServerException, SQLTimeoutException { + @Override + public java.sql.ResultSet getTableTypes() throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); } @@ -1301,12 +1376,14 @@ public java.sql.ResultSet getSchemas(String catalog, return rs; } - /* L0 */ public String getTimeDateFunctions() throws SQLServerException { + @Override + public String getTimeDateFunctions() throws SQLServerException { checkClosed(); return "CURDATE,CURTIME,DAYNAME,DAYOFMONTH,DAYOFWEEK,DAYOFYEAR,HOUR,MINUTE,MONTH,MONTHNAME,NOW,QUARTER,SECOND,TIMESTAMPADD,TIMESTAMPDIFF,WEEK,YEAR"; } - /* L0 */ public java.sql.ResultSet getTypeInfo() throws SQLServerException, SQLTimeoutException { + @Override + public java.sql.ResultSet getTypeInfo() throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); } @@ -1327,9 +1404,11 @@ public java.sql.ResultSet getSchemas(String catalog, return rs; } - /* L0 */ public String getURL() throws SQLServerException { + @Override + public String getURL() throws SQLServerException { checkClosed(); - // Build up the URL with the connection properties do not hand out user ID and password + // Build up the URL with the connection properties do not hand out user + // ID and password StringBuilder url = new StringBuilder(); // get the properties collection from the connection. Properties props = connection.activeConnectionProperties; @@ -1338,7 +1417,8 @@ public java.sql.ResultSet getSchemas(String catalog, String portNumber = null; String instanceName = null; - // build the connection string without the server name, instance name and port number as these go in the front + // build the connection string without the server name, instance name + // and port number as these go in the front int index = info.length; while (--index >= 0) { String name = info[index].name; @@ -1350,7 +1430,8 @@ public java.sql.ResultSet getSchemas(String catalog, String val = info[index].value; // skip empty strings if (0 != val.length()) { - // special case these server name, instance name and port number as these go in the front + // special case these server name, instance name and port + // number as these go in the front if (name.equals(SQLServerDriverStringProperty.SERVER_NAME.toString())) { serverName = val; } @@ -1388,7 +1469,8 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { return (url.toString()); } - /* L0 */ public String getUserName() throws SQLServerException, SQLTimeoutException { + @Override + public String getUserName() throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); } @@ -1420,7 +1502,8 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { private static final String[] getVersionColumnsColumnNames = {/* 1 */ SCOPE, /* 2 */ COLUMN_NAME, /* 3 */ DATA_TYPE, /* 4 */ TYPE_NAME, /* 5 */ COLUMN_SIZE, /* 6 */ BUFFER_LENGTH, /* 7 */ DECIMAL_DIGITS, /* 8 */ PSEUDO_COLUMN}; - /* L0 */ public java.sql.ResultSet getVersionColumns(String catalog, + @Override + public java.sql.ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -1449,338 +1532,405 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { return rs; } - /* L0 */ public boolean isCatalogAtStart() throws SQLServerException { + @Override + public boolean isCatalogAtStart() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean isReadOnly() throws SQLServerException { + @Override + public boolean isReadOnly() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean nullPlusNonNullIsNull() throws SQLServerException { + @Override + public boolean nullPlusNonNullIsNull() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean nullsAreSortedAtEnd() throws SQLServerException { + @Override + public boolean nullsAreSortedAtEnd() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean nullsAreSortedAtStart() throws SQLServerException { + @Override + public boolean nullsAreSortedAtStart() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean nullsAreSortedHigh() throws SQLServerException { + @Override + public boolean nullsAreSortedHigh() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean nullsAreSortedLow() throws SQLServerException { + @Override + public boolean nullsAreSortedLow() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean storesLowerCaseIdentifiers() throws SQLServerException { + @Override + public boolean storesLowerCaseIdentifiers() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean storesLowerCaseQuotedIdentifiers() throws SQLServerException { + @Override + public boolean storesLowerCaseQuotedIdentifiers() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean storesMixedCaseIdentifiers() throws SQLServerException { + @Override + public boolean storesMixedCaseIdentifiers() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean storesMixedCaseQuotedIdentifiers() throws SQLServerException { + @Override + public boolean storesMixedCaseQuotedIdentifiers() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean storesUpperCaseIdentifiers() throws SQLServerException { + @Override + public boolean storesUpperCaseIdentifiers() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean storesUpperCaseQuotedIdentifiers() throws SQLServerException { + @Override + public boolean storesUpperCaseQuotedIdentifiers() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean supportsAlterTableWithAddColumn() throws SQLServerException { + @Override + public boolean supportsAlterTableWithAddColumn() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsAlterTableWithDropColumn() throws SQLServerException { + @Override + public boolean supportsAlterTableWithDropColumn() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsANSI92EntryLevelSQL() throws SQLServerException { + @Override + public boolean supportsANSI92EntryLevelSQL() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsANSI92FullSQL() throws SQLServerException { + @Override + public boolean supportsANSI92FullSQL() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean supportsANSI92IntermediateSQL() throws SQLServerException { + @Override + public boolean supportsANSI92IntermediateSQL() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean supportsCatalogsInDataManipulation() throws SQLServerException { + @Override + public boolean supportsCatalogsInDataManipulation() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsCatalogsInIndexDefinitions() throws SQLServerException { + @Override + public boolean supportsCatalogsInIndexDefinitions() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLServerException { + @Override + public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsCatalogsInProcedureCalls() throws SQLServerException { + @Override + public boolean supportsCatalogsInProcedureCalls() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsCatalogsInTableDefinitions() throws SQLServerException { + @Override + public boolean supportsCatalogsInTableDefinitions() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsColumnAliasing() throws SQLServerException { + @Override + public boolean supportsColumnAliasing() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsConvert() throws SQLServerException { + @Override + public boolean supportsConvert() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsConvert(int fromType, + @Override + public boolean supportsConvert(int fromType, int toType) throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsCoreSQLGrammar() throws SQLServerException { + @Override + public boolean supportsCoreSQLGrammar() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsCorrelatedSubqueries() throws SQLServerException { + @Override + public boolean supportsCorrelatedSubqueries() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLServerException { + @Override + public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsDataManipulationTransactionsOnly() throws SQLServerException { + @Override + public boolean supportsDataManipulationTransactionsOnly() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean supportsDifferentTableCorrelationNames() throws SQLServerException { + @Override + public boolean supportsDifferentTableCorrelationNames() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean supportsExpressionsInOrderBy() throws SQLServerException { + @Override + public boolean supportsExpressionsInOrderBy() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsExtendedSQLGrammar() throws SQLServerException { + @Override + public boolean supportsExtendedSQLGrammar() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean supportsFullOuterJoins() throws SQLServerException { + @Override + public boolean supportsFullOuterJoins() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsGroupBy() throws SQLServerException { + @Override + public boolean supportsGroupBy() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsGroupByBeyondSelect() throws SQLServerException { + @Override + public boolean supportsGroupByBeyondSelect() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsGroupByUnrelated() throws SQLServerException { + @Override + public boolean supportsGroupByUnrelated() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsIntegrityEnhancementFacility() throws SQLServerException { + @Override + public boolean supportsIntegrityEnhancementFacility() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean supportsLikeEscapeClause() throws SQLServerException { + @Override + public boolean supportsLikeEscapeClause() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsLimitedOuterJoins() throws SQLServerException { + @Override + public boolean supportsLimitedOuterJoins() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsMinimumSQLGrammar() throws SQLServerException { + @Override + public boolean supportsMinimumSQLGrammar() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsMixedCaseIdentifiers() throws SQLServerException { + @Override + public boolean supportsMixedCaseIdentifiers() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsMixedCaseQuotedIdentifiers() throws SQLServerException { + @Override + public boolean supportsMixedCaseQuotedIdentifiers() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsMultipleResultSets() throws SQLServerException { + @Override + public boolean supportsMultipleResultSets() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsMultipleTransactions() throws SQLServerException { + @Override + public boolean supportsMultipleTransactions() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsNonNullableColumns() throws SQLServerException { + @Override + public boolean supportsNonNullableColumns() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsOpenCursorsAcrossCommit() throws SQLServerException { + @Override + public boolean supportsOpenCursorsAcrossCommit() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean supportsOpenCursorsAcrossRollback() throws SQLServerException { + @Override + public boolean supportsOpenCursorsAcrossRollback() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean supportsOpenStatementsAcrossCommit() throws SQLServerException { + @Override + public boolean supportsOpenStatementsAcrossCommit() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsOpenStatementsAcrossRollback() throws SQLServerException { + @Override + public boolean supportsOpenStatementsAcrossRollback() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsOrderByUnrelated() throws SQLServerException { + @Override + public boolean supportsOrderByUnrelated() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsOuterJoins() throws SQLServerException { + @Override + public boolean supportsOuterJoins() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsPositionedDelete() throws SQLServerException { + @Override + public boolean supportsPositionedDelete() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsPositionedUpdate() throws SQLServerException { + @Override + public boolean supportsPositionedUpdate() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsSchemasInDataManipulation() throws SQLServerException { + @Override + public boolean supportsSchemasInDataManipulation() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsSchemasInIndexDefinitions() throws SQLServerException { + @Override + public boolean supportsSchemasInIndexDefinitions() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsSchemasInPrivilegeDefinitions() throws SQLServerException { + @Override + public boolean supportsSchemasInPrivilegeDefinitions() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsSchemasInProcedureCalls() throws SQLServerException { + @Override + public boolean supportsSchemasInProcedureCalls() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsSchemasInTableDefinitions() throws SQLServerException { + @Override + public boolean supportsSchemasInTableDefinitions() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsSelectForUpdate() throws SQLServerException { + @Override + public boolean supportsSelectForUpdate() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean supportsStoredProcedures() throws SQLServerException { + @Override + public boolean supportsStoredProcedures() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsSubqueriesInComparisons() throws SQLServerException { + @Override + public boolean supportsSubqueriesInComparisons() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsSubqueriesInExists() throws SQLServerException { + @Override + public boolean supportsSubqueriesInExists() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsSubqueriesInIns() throws SQLServerException { + @Override + public boolean supportsSubqueriesInIns() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsSubqueriesInQuantifieds() throws SQLServerException { + @Override + public boolean supportsSubqueriesInQuantifieds() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsTableCorrelationNames() throws SQLServerException { + @Override + public boolean supportsTableCorrelationNames() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsTransactionIsolationLevel(int level) throws SQLServerException { + @Override + public boolean supportsTransactionIsolationLevel(int level) throws SQLServerException { checkClosed(); switch (level) { case Connection.TRANSACTION_READ_UNCOMMITTED: @@ -1793,40 +1943,48 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { return false; } - /* L0 */ public boolean supportsTransactions() throws SQLServerException { + @Override + public boolean supportsTransactions() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsUnion() throws SQLServerException { + @Override + public boolean supportsUnion() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean supportsUnionAll() throws SQLServerException { + @Override + public boolean supportsUnionAll() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public boolean usesLocalFilePerTable() throws SQLServerException { + @Override + public boolean usesLocalFilePerTable() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean usesLocalFiles() throws SQLServerException { + @Override + public boolean usesLocalFiles() throws SQLServerException { checkClosed(); return false; } - /* L0 */ public boolean supportsResultSetType(int type) throws SQLServerException { + @Override + public boolean supportsResultSetType(int type) throws SQLServerException { checkClosed(); checkResultType(type); switch (type) { case ResultSet.TYPE_FORWARD_ONLY: case ResultSet.TYPE_SCROLL_INSENSITIVE: case ResultSet.TYPE_SCROLL_SENSITIVE: - // case SQLServerResultSet.TYPE_SS_SCROLL_STATIC: insensitive synonym - // case SQLServerResultSet.TYPE_SS_SCROLL_KEYSET: sensitive synonym + // case SQLServerResultSet.TYPE_SS_SCROLL_STATIC: insensitive + // synonym + // case SQLServerResultSet.TYPE_SS_SCROLL_KEYSET: sensitive + // synonym case SQLServerResultSet.TYPE_SS_DIRECT_FORWARD_ONLY: case SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY: case SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC: @@ -1835,7 +1993,8 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { return false; } - /* L0 */ public boolean supportsResultSetConcurrency(int type, + @Override + public boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLServerException { checkClosed(); checkResultType(type); @@ -1843,12 +2002,14 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { switch (type) { case ResultSet.TYPE_FORWARD_ONLY: case ResultSet.TYPE_SCROLL_SENSITIVE: - // case SQLServerResultSet.TYPE_SS_SCROLL_KEYSET: sensitive synonym + // case SQLServerResultSet.TYPE_SS_SCROLL_KEYSET: sensitive + // synonym case SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC: case SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY: return true; case ResultSet.TYPE_SCROLL_INSENSITIVE: - // case SQLServerResultSet.TYPE_SS_SCROLL_STATIC: sensitive synonym + // case SQLServerResultSet.TYPE_SS_SCROLL_STATIC: sensitive + // synonym case SQLServerResultSet.TYPE_SS_DIRECT_FORWARD_ONLY: return (ResultSet.CONCUR_READ_ONLY == concurrency); } @@ -1856,7 +2017,8 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { return false; } - /* L0 */ public boolean ownUpdatesAreVisible(int type) throws SQLServerException { + @Override + public boolean ownUpdatesAreVisible(int type) throws SQLServerException { checkClosed(); checkResultType(type); return (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type @@ -1864,7 +2026,8 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type); } - /* L0 */ public boolean ownDeletesAreVisible(int type) throws SQLServerException { + @Override + public boolean ownDeletesAreVisible(int type) throws SQLServerException { checkClosed(); checkResultType(type); return (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type @@ -1872,7 +2035,8 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type); } - /* L0 */ public boolean ownInsertsAreVisible(int type) throws SQLServerException { + @Override + public boolean ownInsertsAreVisible(int type) throws SQLServerException { checkClosed(); checkResultType(type); return (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type @@ -1880,7 +2044,8 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type); } - /* L0 */ public boolean othersUpdatesAreVisible(int type) throws SQLServerException { + @Override + public boolean othersUpdatesAreVisible(int type) throws SQLServerException { checkClosed(); checkResultType(type); return (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type @@ -1888,7 +2053,8 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type); } - /* L0 */ public boolean othersDeletesAreVisible(int type) throws SQLServerException { + @Override + public boolean othersDeletesAreVisible(int type) throws SQLServerException { checkClosed(); checkResultType(type); return (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type @@ -1896,33 +2062,38 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type); } - /* L0 */ public boolean othersInsertsAreVisible(int type) throws SQLServerException { + @Override + public boolean othersInsertsAreVisible(int type) throws SQLServerException { checkClosed(); checkResultType(type); return (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type); } - /* L0 */ public boolean updatesAreDetected(int type) throws SQLServerException { + @Override + public boolean updatesAreDetected(int type) throws SQLServerException { checkClosed(); checkResultType(type); return false; } - /* L0 */ public boolean deletesAreDetected(int type) throws SQLServerException { + @Override + public boolean deletesAreDetected(int type) throws SQLServerException { checkClosed(); checkResultType(type); return (SQLServerResultSet.TYPE_SS_SCROLL_KEYSET == type); } // Check the result types to make sure the user does not pass a bad value. - /* L0 */ private void checkResultType(int type) throws SQLServerException { + private void checkResultType(int type) throws SQLServerException { switch (type) { case ResultSet.TYPE_FORWARD_ONLY: case ResultSet.TYPE_SCROLL_INSENSITIVE: case ResultSet.TYPE_SCROLL_SENSITIVE: - // case SQLServerResultSet.TYPE_SS_SCROLL_STATIC: synonym TYPE_SCROLL_INSENSITIVE - // case SQLServerResultSet.TYPE_SS_SCROLL_KEYSET: synonym TYPE_SCROLL_SENSITIVE + // case SQLServerResultSet.TYPE_SS_SCROLL_STATIC: synonym + // TYPE_SCROLL_INSENSITIVE + // case SQLServerResultSet.TYPE_SS_SCROLL_KEYSET: synonym + // TYPE_SCROLL_SENSITIVE case SQLServerResultSet.TYPE_SS_DIRECT_FORWARD_ONLY: case SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY: case SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC: @@ -1934,12 +2105,14 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { throw new SQLServerException(null, form.format(msgArgs), null, 0, true); } - // Check the concurrency values and make sure the value is a supported value. - /* L0 */ private void checkConcurrencyType(int type) throws SQLServerException { + // Check the concurrency values and make sure the value is a supported + // value. + private void checkConcurrencyType(int type) throws SQLServerException { switch (type) { case ResultSet.CONCUR_READ_ONLY: case ResultSet.CONCUR_UPDATABLE: - // case SQLServerResultSet.CONCUR_SS_OPTIMISTIC_CC: synonym CONCUR_UPDATABLE + // case SQLServerResultSet.CONCUR_SS_OPTIMISTIC_CC: synonym + // CONCUR_UPDATABLE case SQLServerResultSet.CONCUR_SS_SCROLL_LOCKS: case SQLServerResultSet.CONCUR_SS_OPTIMISTIC_CCVAL: return; @@ -1950,18 +2123,21 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { throw new SQLServerException(null, form.format(msgArgs), null, 0, true); } - /* L0 */ public boolean insertsAreDetected(int type) throws SQLServerException { + @Override + public boolean insertsAreDetected(int type) throws SQLServerException { checkClosed(); checkResultType(type); return false; } - /* L0 */ public boolean supportsBatchUpdates() throws SQLServerException { + @Override + public boolean supportsBatchUpdates() throws SQLServerException { checkClosed(); return true; } - /* L0 */ public java.sql.ResultSet getUDTs(String catalog, + @Override + public java.sql.ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePattern, int[] types) throws SQLServerException, SQLTimeoutException { @@ -1979,14 +2155,16 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { /* 7 */ " cast(0 as smallint) as BASE_TYPE" + " where 0 = 1"); } - /* L0 */ public java.sql.Connection getConnection() throws SQLServerException { + @Override + public java.sql.Connection getConnection() throws SQLServerException { checkClosed(); return connection.getConnection(); } /* JDBC 3.0 */ - /* L3 */ public int getSQLStateType() throws SQLServerException { + @Override + public int getSQLStateType() throws SQLServerException { checkClosed(); if (connection != null && connection.xopenStates) return sqlStateXOpen; @@ -1994,7 +2172,8 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { return sqlStateSQL99; } - /* L3 */ public int getDatabaseMajorVersion() throws SQLServerException { + @Override + public int getDatabaseMajorVersion() throws SQLServerException { checkClosed(); String s = connection.sqlServerVersion; int p = s.indexOf('.'); @@ -2008,7 +2187,8 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { } } - /* L3 */ public int getDatabaseMinorVersion() throws SQLServerException { + @Override + public int getDatabaseMinorVersion() throws SQLServerException { checkClosed(); String s = connection.sqlServerVersion; int p = s.indexOf('.'); @@ -2023,30 +2203,37 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { } } - /* L3 */ public int getJDBCMajorVersion() throws SQLServerException { + @Override + public int getJDBCMajorVersion() throws SQLServerException { checkClosed(); return DriverJDBCVersion.major; } - /* L3 */ public int getJDBCMinorVersion() throws SQLServerException { + @Override + public int getJDBCMinorVersion() throws SQLServerException { checkClosed(); return DriverJDBCVersion.minor; } - /* L3 */ public int getResultSetHoldability() throws SQLServerException { + @Override + public int getResultSetHoldability() throws SQLServerException { checkClosed(); - return ResultSet.HOLD_CURSORS_OVER_COMMIT; // Hold over commit is the default for SQL Server + return ResultSet.HOLD_CURSORS_OVER_COMMIT; // Hold over commit is the + // default for SQL Server } + @Override public RowIdLifetime getRowIdLifetime() throws SQLException { checkClosed(); return RowIdLifetime.ROWID_UNSUPPORTED; } - /* L3 */ public boolean supportsResultSetHoldability(int holdability) throws SQLServerException { + @Override + public boolean supportsResultSetHoldability(int holdability) throws SQLServerException { checkClosed(); if (ResultSet.HOLD_CURSORS_OVER_COMMIT == holdability || ResultSet.CLOSE_CURSORS_AT_COMMIT == holdability) { - return true; // supported one a per connection level only, not statement by statement + return true; // supported one a per connection level only, not + // statement by statement } // if the value is outside of the valid values throw error. @@ -2055,7 +2242,8 @@ public RowIdLifetime getRowIdLifetime() throws SQLException { throw new SQLServerException(null, form.format(msgArgs), null, 0, true); } - /* L3 */ public ResultSet getAttributes(String catalog, + @Override + public ResultSet getAttributes(String catalog, String schemaPattern, String typeNamePattern, String attributeNamePattern) throws SQLServerException, SQLTimeoutException { @@ -2087,7 +2275,8 @@ public RowIdLifetime getRowIdLifetime() throws SQLException { /* 21 */ " cast(0 as smallint) as SOURCE_DATA_TYPE" + " where 0 = 1"); } - /* L3 */ public ResultSet getSuperTables(String catalog, + @Override + public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern) throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -2101,7 +2290,8 @@ public RowIdLifetime getRowIdLifetime() throws SQLException { /* 4 */ " cast(NULL as char(1)) as SUPERTABLE_NAME" + " where 0 = 1"); } - /* L3 */ public ResultSet getSuperTypes(String catalog, + @Override + public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern) throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -2117,37 +2307,44 @@ public RowIdLifetime getRowIdLifetime() throws SQLException { /* 6 */ " cast(NULL as char(1)) as SUPERTYPE_NAME" + " where 0 = 1"); } - /* L3 */ public boolean supportsGetGeneratedKeys() throws SQLServerException { + @Override + public boolean supportsGetGeneratedKeys() throws SQLServerException { checkClosed(); return true; } - /* L3 */ public boolean supportsMultipleOpenResults() throws SQLServerException { + @Override + public boolean supportsMultipleOpenResults() throws SQLServerException { checkClosed(); return false; } - /* L3 */ public boolean supportsNamedParameters() throws SQLServerException { + @Override + public boolean supportsNamedParameters() throws SQLServerException { checkClosed(); return true; } - /* L3 */ public boolean supportsSavepoints() throws SQLServerException { + @Override + public boolean supportsSavepoints() throws SQLServerException { checkClosed(); return true; } - /* L3 */ public boolean supportsStatementPooling() throws SQLException { + @Override + public boolean supportsStatementPooling() throws SQLException { checkClosed(); return false; } + @Override public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException { checkClosed(); return true; } - /* L3 */ public boolean locatorsUpdateCopy() throws SQLException { + @Override + public boolean locatorsUpdateCopy() throws SQLException { checkClosed(); return true; } @@ -2200,7 +2397,8 @@ int oneValueToAnother(int precl) { } } -// abstract class converts one value to another solely based on the column integer value +// abstract class converts one value to another solely based on the column +// integer value // apply to integer columns only abstract class IntColumnFilter extends ColumnFilter { abstract int oneValueToAnother(int value); @@ -2209,8 +2407,10 @@ final Object apply(Object value, JDBCType asJDBCType) throws SQLServerException { if (value == null) return value; - // Assumption: values will only be requested in integral or textual format - // (i.e. not as float, double, BigDecimal, Boolean or bytes). A request to return + // Assumption: values will only be requested in integral or textual + // format + // (i.e. not as float, double, BigDecimal, Boolean or bytes). A request + // to return // a value as anything else results in an exception being thrown. switch (asJDBCType) { @@ -2245,18 +2445,25 @@ final Object apply(Object value, JDBCType asJDBCType) throws SQLServerException { if (value == null) return value; - // Assumption: values will only be requested in integral or textual format - // (i.e. not as float, double, BigDecimal, Boolean or bytes). A request to return + // Assumption: values will only be requested in integral or textual + // format + // (i.e. not as float, double, BigDecimal, Boolean or bytes). A request + // to return // a value as anything else results in an exception being thrown. switch (asJDBCType) { case INTEGER: case SMALLINT: - // This is a way for us to make getObject return a string, not an - // integer. What this means is that getInt/getShort also will return a string. - // However the identity column in the JDBC spec is supposed to return a - // string by default. To get to that default behavior right we are deliberately breaking - // the getInt/getShort behavior which should really error anyways. Only thing is that + // This is a way for us to make getObject return a string, not + // an + // integer. What this means is that getInt/getShort also will + // return a string. + // However the identity column in the JDBC spec is supposed to + // return a + // string by default. To get to that default behavior right we + // are deliberately breaking + // the getInt/getShort behavior which should really error + // anyways. Only thing is that // the user will get a cast exception in this case. assert (value instanceof Number); return zeroOneToYesNo(((Number) value).intValue()); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java index 52d80a4a2..18cf1bbac 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java @@ -8,6 +8,7 @@ package com.microsoft.sqlserver.jdbc; +import java.sql.SQLFeatureNotSupportedException; import java.text.MessageFormat; import java.util.UUID; import java.util.logging.Level; @@ -97,7 +98,7 @@ final void setDriverErrorCode(int value) { * @param bStack * true to generate the stack trace */ - /* L0 */ private void logException(Object o, + private void logException(Object o, String errText, boolean bStack) { String id = ""; @@ -110,19 +111,20 @@ final void setDriverErrorCode(int value) { if (exLogger.isLoggable(Level.FINE)) { StringBuilder sb = new StringBuilder(100); StackTraceElement st[] = this.getStackTrace(); - for (StackTraceElement aSt : st) sb.append(aSt.toString()); + for (StackTraceElement aSt : st) + sb.append(aSt.toString()); Throwable t = this.getCause(); if (t != null) { sb.append("\n caused by " + t + "\n"); StackTraceElement tst[] = t.getStackTrace(); - for (StackTraceElement aTst : tst) sb.append(aTst.toString()); + for (StackTraceElement aTst : tst) + sb.append(aTst.toString()); } exLogger.fine(sb.toString()); } } - if (errText.equals(SQLServerException.getErrString("R_queryTimedOut"))) - { - this.setDriverErrorCode(SQLServerException.ERROR_QUERY_TIMEOUT); + if (errText.equals(SQLServerException.getErrString("R_queryTimedOut"))) { + this.setDriverErrorCode(SQLServerException.ERROR_QUERY_TIMEOUT); } } @@ -402,9 +404,16 @@ static String checkAndAppendClientConnId(String errMsg, sb.append(clientConnId.toString()); return sb.toString(); } - else + else { return errMsg; - + } } + static void throwNotSupportedException(SQLServerConnection con, Object obj) throws SQLServerException { + SQLServerException.makeFromDriverError(con, obj, SQLServerException.getErrString("R_notSupported"), null, false); + } + + static void throwFeatureNotSupportedException() throws SQLFeatureNotSupportedException { + throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java index 1950daf62..c37a16729 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java @@ -22,9 +22,6 @@ final class DriverJDBCVersion { static final int major = 4; static final int minor = 2; - static final void checkSupportsJDBC42() { - } - static final void checkSupportsJDBC43() { throw new UnsupportedOperationException(SQLServerException.getErrString("R_notSupported")); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java index 1f60cd53a..1100f132d 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java @@ -24,9 +24,6 @@ final class DriverJDBCVersion { static final void checkSupportsJDBC43() { } - - static final void checkSupportsJDBC42() { - } static final void throwBatchUpdateException(SQLServerException lastError, long[] updateCounts) throws BatchUpdateException { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerLob.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerLob.java index 3181cd47d..1849915be 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerLob.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerLob.java @@ -9,6 +9,12 @@ import java.sql.SQLException; -abstract class SQLServerLob { - abstract void fillFromStream() throws SQLException; +abstract class SQLServerLob { + + /** + * Function for the result set to maintain blobs it has created + * + * @throws SQLException + */ + abstract void fillFromStream() throws SQLException; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerNClob.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerNClob.java index 4dbf20017..f3930381c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerNClob.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerNClob.java @@ -1,38 +1,127 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.jdbc; - -import java.io.UnsupportedEncodingException; -import java.sql.NClob; -import java.util.logging.Logger; - -/** - * SQLServerNClob represents a National Character Set LOB object and implements java.sql.NClob. - */ - -public final class SQLServerNClob extends SQLServerClobBase implements NClob { - - private static final long serialVersionUID = 1L; - - // Loggers should be class static to avoid lock contention with multiple threads - private static final Logger logger = Logger.getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerNClob"); - - SQLServerNClob(SQLServerConnection connection) { - super(connection, "", connection.getDatabaseCollation(), logger, null); - } - - SQLServerNClob(BaseInputStream stream, - TypeInfo typeInfo) throws SQLServerException, UnsupportedEncodingException { - super(null, stream, typeInfo.getSQLCollation(), logger, typeInfo); - } - - final JDBCType getJdbcType() { - return JDBCType.NCLOB; - } -} +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + +package com.microsoft.sqlserver.jdbc; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.sql.Clob; +import java.sql.NClob; +import java.sql.SQLException; +import java.util.logging.Logger; + +/** + * SQLServerNClob represents a National Character Set LOB object and implements java.sql.NClob. + */ + +public final class SQLServerNClob extends SQLServerClobBase implements NClob { + + /** + * Always refresh SerialVersionUID when prompted + */ + private static final long serialVersionUID = 3593610902551842327L; + + // Loggers should be class static to avoid lock contention with multiple + // threads + private static final Logger logger = Logger.getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerNClob"); + + SQLServerNClob(SQLServerConnection connection) { + super(connection, "", connection.getDatabaseCollation(), logger, null); + } + + SQLServerNClob(BaseInputStream stream, + TypeInfo typeInfo) throws SQLServerException, UnsupportedEncodingException { + super(null, stream, typeInfo.getSQLCollation(), logger, typeInfo); + } + + @Override + public void free() throws SQLException { + super.free(); + } + + @Override + public InputStream getAsciiStream() throws SQLException { + return super.getAsciiStream(); + } + + @Override + public Reader getCharacterStream() throws SQLException { + return super.getCharacterStream(); + } + + @Override + public Reader getCharacterStream(long pos, + long length) throws SQLException { + return super.getCharacterStream(pos, length); + } + + @Override + public String getSubString(long pos, + int length) throws SQLException { + return super.getSubString(pos, length); + } + + @Override + public long length() throws SQLException { + return super.length(); + } + + @Override + void fillFromStream() throws SQLException { + super.fillFromStream(); + } + + @Override + public long position(Clob searchstr, + long start) throws SQLException { + return super.position(searchstr, start); + } + + @Override + public long position(String searchstr, + long start) throws SQLException { + return super.position(searchstr, start); + } + + @Override + public void truncate(long len) throws SQLException { + super.truncate(len); + } + + @Override + public OutputStream setAsciiStream(long pos) throws SQLException { + return super.setAsciiStream(pos); + } + + @Override + public Writer setCharacterStream(long pos) throws SQLException { + return super.setCharacterStream(pos); + } + + @Override + public int setString(long pos, + String s) throws SQLException { + return super.setString(pos, s); + } + + @Override + public int setString(long pos, + String str, + int offset, + int len) throws SQLException { + return super.setString(pos, str, offset, len); + } + + @Override + final JDBCType getJdbcType() { + return JDBCType.NCLOB; + } +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java index dca580ec9..f9ebd3b1c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java @@ -66,6 +66,7 @@ private static int nextInstanceID() { * * @return traceID string */ + @Override final public String toString() { return traceID; } @@ -79,7 +80,7 @@ final public String toString() { * the token that prfixes the column set * @throws SQLServerException */ - /* L2 */ private String parseColumns(String columnSet, + private String parseColumns(String columnSet, String columnStartToken) throws SQLServerException { StringTokenizer st = new StringTokenizer(columnSet, " =?<>!\r\n\t\f", true); final int START = 0; @@ -141,7 +142,7 @@ final public String toString() { * the token that denotes the start of the column set * @throws SQLServerException */ - /* L2 */ private String parseInsertColumns(String sql, + private String parseInsertColumns(String sql, String columnMarker) throws SQLServerException { StringTokenizer st = new StringTokenizer(sql, " (),", true); int nState = 0; @@ -685,11 +686,13 @@ private void checkClosed() throws SQLServerException { } } + @Override public boolean isWrapperFor(Class iface) throws SQLException { boolean f = iface.isInstance(this); return f; } + @Override public T unwrap(Class iface) throws SQLException { T t; try { @@ -701,7 +704,7 @@ public T unwrap(Class iface) throws SQLException { return t; } - /* L2 */ private void verifyParameterPosition(int param) throws SQLServerException { + private void verifyParameterPosition(int param) throws SQLServerException { boolean bFound = false; try { if (((SQLServerPreparedStatement) stmtParent).bReturnValueSyntax && isTVP) { @@ -723,13 +726,14 @@ public T unwrap(Class iface) throws SQLException { } } - /* L2 */ private void checkParam(int n) throws SQLServerException { + private void checkParam(int n) throws SQLServerException { if (!queryMetaMap.containsKey(n)) { SQLServerException.makeFromDriverError(con, stmtParent, SQLServerException.getErrString("R_noMetadata"), null, false); } } - /* L2 */ public String getParameterClassName(int param) throws SQLServerException { + @Override + public String getParameterClassName(int param) throws SQLServerException { checkClosed(); try { if (rsProcedureMeta == null) { @@ -749,7 +753,8 @@ public T unwrap(Class iface) throws SQLException { } } - /* L2 */ public int getParameterCount() throws SQLServerException { + @Override + public int getParameterCount() throws SQLServerException { checkClosed(); try { if (rsProcedureMeta == null) { @@ -770,7 +775,8 @@ public T unwrap(Class iface) throws SQLException { } } - /* L2 */ public int getParameterMode(int param) throws SQLServerException { + @Override + public int getParameterMode(int param) throws SQLServerException { checkClosed(); try { if (rsProcedureMeta == null) { @@ -797,7 +803,8 @@ public T unwrap(Class iface) throws SQLException { } } - /* L2 */ public int getParameterType(int param) throws SQLServerException { + @Override + public int getParameterType(int param) throws SQLServerException { checkClosed(); int parameterType; @@ -834,7 +841,8 @@ public T unwrap(Class iface) throws SQLException { } } - /* L2 */ public String getParameterTypeName(int param) throws SQLServerException { + @Override + public String getParameterTypeName(int param) throws SQLServerException { checkClosed(); try { if (rsProcedureMeta == null) { @@ -853,7 +861,8 @@ public T unwrap(Class iface) throws SQLException { } } - /* L2 */ public int getPrecision(int param) throws SQLServerException { + @Override + public int getPrecision(int param) throws SQLServerException { checkClosed(); try { if (rsProcedureMeta == null) { @@ -873,7 +882,8 @@ public T unwrap(Class iface) throws SQLException { } } - /* L2 */ public int getScale(int param) throws SQLServerException { + @Override + public int getScale(int param) throws SQLServerException { checkClosed(); try { if (rsProcedureMeta == null) { @@ -893,7 +903,8 @@ public T unwrap(Class iface) throws SQLException { } } - /* L2 */ public int isNullable(int param) throws SQLServerException { + @Override + public int isNullable(int param) throws SQLServerException { checkClosed(); try { if (rsProcedureMeta == null) { @@ -926,7 +937,8 @@ public T unwrap(Class iface) throws SQLException { * when an error occurs * @return boolean */ - /* L2 */ public boolean isSigned(int param) throws SQLServerException { + @Override + public boolean isSigned(int param) throws SQLServerException { checkClosed(); try { if (rsProcedureMeta == null) { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPooledConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPooledConnection.java index cc059434d..f17368e6f 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPooledConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPooledConnection.java @@ -63,6 +63,7 @@ public class SQLServerPooledConnection implements PooledConnection { * * @return traceID String */ + @Override public String toString() { return traceID; } @@ -79,6 +80,7 @@ private SQLServerConnection createNewConnection() throws SQLException { * when an error occurs * @return a Connection object that is a handle to this PooledConnection object */ + @Override public Connection getConnection() throws SQLException { if (pcLogger.isLoggable(Level.FINER)) pcLogger.finer(toString() + " user:(default)."); @@ -161,6 +163,7 @@ void notifyEvent(SQLServerException e) { } } + @Override public void addConnectionEventListener(ConnectionEventListener listener) { if (pcLogger.isLoggable(Level.FINER)) pcLogger.finer(toString() + safeCID()); @@ -169,6 +172,7 @@ public void addConnectionEventListener(ConnectionEventListener listener) { } } + @Override public void close() throws SQLException { if (pcLogger.isLoggable(Level.FINER)) pcLogger.finer(toString() + " Closing physical connection, " + safeCID()); @@ -189,6 +193,7 @@ public void close() throws SQLException { } + @Override public void removeConnectionEventListener(ConnectionEventListener listener) { if (pcLogger.isLoggable(Level.FINER)) pcLogger.finer(toString() + safeCID()); @@ -197,11 +202,13 @@ public void removeConnectionEventListener(ConnectionEventListener listener) { } } + @Override public void addStatementEventListener(StatementEventListener listener) { // Not implemented throw new UnsupportedOperationException(SQLServerException.getErrString("R_notSupported")); } + @Override public void removeStatementEventListener(StatementEventListener listener) { // Not implemented throw new UnsupportedOperationException(SQLServerException.getErrString("R_notSupported")); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index ac6289f2f..063dabe39 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -6,7 +6,7 @@ * 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; +package com.microsoft.sqlserver.jdbc; import static com.microsoft.sqlserver.jdbc.SQLServerConnection.getCachedParsedSQL; import static com.microsoft.sqlserver.jdbc.SQLServerConnection.parseAndCacheSQL; @@ -20,8 +20,8 @@ import java.sql.ResultSet; import java.sql.RowId; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLTimeoutException; +import java.sql.SQLType; import java.sql.SQLXML; import java.sql.Statement; import java.text.MessageFormat; @@ -54,6 +54,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS boolean isInternalEncryptionQuery = false; /** delimiter for multiple statements in a single batch */ + @SuppressWarnings("unused") private static final int BATCH_STATEMENT_DELIMITER_TDS_71 = 0x80; private static final int BATCH_STATEMENT_DELIMITER_TDS_72 = 0xFF; final int nBatchStatementDelimiter = BATCH_STATEMENT_DELIMITER_TDS_72; @@ -71,7 +72,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS private boolean isExecutedAtLeastOnce = false; /** Reference to cache item for statement handle pooling. Only used to decrement ref count on statement close. */ - private PreparedStatementHandle cachedPreparedStatementHandle; + private PreparedStatementHandle cachedPreparedStatementHandle; /** Hash of user supplied SQL statement used for various cache lookups */ private Sha1HashKey sqlTextCacheKey; @@ -99,7 +100,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS /** The prepared statement handle returned by the server */ private int prepStmtHandle = 0; - + /** Statement used for getMetadata(). Declared as a field to facilitate closing the statement. */ private SQLServerStatement internalStmt = null; @@ -136,22 +137,17 @@ private void setUseBulkCopyForBatchInsert(boolean useBulkCopyForBatchInsert) thr this.useBulkCopyForBatchInsert = useBulkCopyForBatchInsert; } - /** The server handle for this prepared statement. If a value {@literal <} 1 is returned no handle has been created. - * - * @return - * Per the description. - * @throws SQLServerException when an error occurs - */ + @Override public int getPreparedStatementHandle() throws SQLServerException { - checkClosed(); + checkClosed(); return prepStmtHandle; } - /** Returns true if this statement has a server handle. - * - * @return - * Per the description. - */ + /** + * Returns true if this statement has a server handle. + * + * @return Per the description. + */ private boolean hasPreparedStatementHandle() { return 0 < prepStmtHandle; } @@ -170,10 +166,10 @@ private boolean resetPrepStmtHandle(boolean discardCurrentCacheItem) { prepStmtHandle = 0; return statementPoolingUsed; } - + /** Flag set to true when statement execution is expected to return the prepared statement handle */ private boolean expectPrepStmtHandle = false; - + /** * Flag set to true when all encryption metadata of inOutParam is retrieved */ @@ -222,8 +218,8 @@ String getClassNameInternal() { // Parse or fetch SQL metadata from cache. ParsedSQLCacheItem parsedSQL = getCachedParsedSQL(sqlTextCacheKey); - if(null != parsedSQL) { - if(null != connection && connection.isStatementPoolingEnabled()) { + if (null != parsedSQL) { + if (null != connection && connection.isStatementPoolingEnabled()) { isExecutedAtLeastOnce = true; } } @@ -256,14 +252,13 @@ private void closePreparedHandle() { else { isExecutedAtLeastOnce = false; final int handleToClose = prepStmtHandle; - // Handle unprepare actions through statement pooling. if (resetPrepStmtHandle(false)) { connection.returnCachedPreparedStatementHandle(cachedPreparedStatementHandle); } - // If no reference to a statement pool cache item is found handle unprepare actions through batching @ connection level. - else if(connection.isPreparedStatementUnprepareBatchingEnabled()) { + // If no reference to a statement pool cache item is found handle unprepare actions through batching @ connection level. + else if (connection.isPreparedStatementUnprepareBatchingEnabled()) { connection.enqueueUnprepareStatementHandle(connection.new PreparedStatementHandle(null, handleToClose, executedSqlDirectly, true)); } else { @@ -318,12 +313,13 @@ final void closeInternal() { // If we have a prepared statement handle, close it. closePreparedHandle(); - + // Close the statement that was used to generate empty statement from getMetadata(). try { if (null != internalStmt) internalStmt.close(); - } catch (SQLServerException e) { + } + catch (SQLServerException e) { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.finer("Ignored error closing internal statement: " + e.getErrorCode() + " " + e.getMessage()); } @@ -335,20 +331,21 @@ final void closeInternal() { batchParamValues = null; } - /** + /** * Intialize the statement parameters. * - * @param nParams - * Number of parameters to Intialize. + * @param nParams + * Number of parameters to Intialize. */ - /* L0 */ final void initParams(int nParams) { + final void initParams(int nParams) { inOutParam = new Parameter[nParams]; for (int i = 0; i < nParams; i++) { inOutParam[i] = new Parameter(Util.shouldHonorAEForParameters(stmtColumnEncriptionSetting, connection)); } } - /* L0 */ public final void clearParameters() throws SQLServerException { + @Override + public final void clearParameters() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "clearParameters"); checkClosed(); encryptionMetadataIsRetrieved = false; @@ -369,7 +366,7 @@ private boolean buildPreparedStrings(Parameter[] params, boolean renewDefinition) throws SQLServerException { String newTypeDefinitions = buildParamTypeDefinitions(params, renewDefinition); if (null != preparedTypeDefinitions && newTypeDefinitions.equalsIgnoreCase(preparedTypeDefinitions)) - return false; + return false; preparedTypeDefinitions = newTypeDefinitions; @@ -426,15 +423,7 @@ private String buildParamTypeDefinitions(Parameter[] params, return sb.toString(); } - /** - * Execute a query. - * - * @throws SQLServerException - * when an error occurs - * @throws SQLTimeoutException - * when the query times out - * @return ResultSet - */ + @Override public java.sql.ResultSet executeQuery() throws SQLServerException, SQLTimeoutException { loggerExternal.entering(getClassNameLogging(), "executeQuery"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -451,7 +440,7 @@ public java.sql.ResultSet executeQuery() throws SQLServerException, SQLTimeoutEx * * @throws SQLServerException * @return ResultSet - * @throws SQLTimeoutException + * @throws SQLTimeoutException */ final java.sql.ResultSet executeQueryInternal() throws SQLServerException, SQLTimeoutException { checkClosed(); @@ -459,6 +448,7 @@ final java.sql.ResultSet executeQueryInternal() throws SQLServerException, SQLTi return resultSet; } + @Override public int executeUpdate() throws SQLServerException, SQLTimeoutException { loggerExternal.entering(getClassNameLogging(), "executeUpdate"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -478,8 +468,8 @@ public int executeUpdate() throws SQLServerException, SQLTimeoutException { return (int) updateCount; } + @Override public long executeLargeUpdate() throws SQLServerException, SQLTimeoutException { - DriverJDBCVersion.checkSupportsJDBC42(); loggerExternal.entering(getClassNameLogging(), "executeLargeUpdate"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -491,15 +481,7 @@ public long executeLargeUpdate() throws SQLServerException, SQLTimeoutException return updateCount; } - /** - * Execute a query or non query statement. - * - * @throws SQLServerException - * when an error occurs - * @throws SQLTimeoutException - * when the query times out - * @return true if the statement returned a result set - */ + @Override public boolean execute() throws SQLServerException, SQLTimeoutException { loggerExternal.entering(getClassNameLogging(), "execute"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -599,7 +581,7 @@ final void doExecutePreparedStatement(PrepStmtExecCmd command) throws SQLServerE throw e; } break; - } + } if (EXECUTE_QUERY == executeMethod && null == resultSet) { SQLServerException.makeFromDriverError(connection, this, SQLServerException.getErrString("R_noResultset"), null, true); @@ -608,7 +590,7 @@ else if (EXECUTE_UPDATE == executeMethod && null != resultSet) { SQLServerException.makeFromDriverError(connection, this, SQLServerException.getErrString("R_resultsetGeneratedForUpdate"), null, false); } } - + /** Should the execution be retried because the re-used cached handle could not be re-used due to server side state changes? */ private boolean retryBasedOnFailedReuseOfCachedHandle(SQLException e, int attempt, @@ -651,7 +633,7 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException { cachedPreparedStatementHandle = connection.registerCachedPreparedStatementHandle( new Sha1HashKey(preparedSQL, preparedTypeDefinitions), prepStmtHandle, executedSqlDirectly); } - + param.skipValue(tdsReader, true); if (getStatementLogger().isLoggable(java.util.logging.Level.FINER)) getStatementLogger().finer(toString() + ": Setting PreparedHandle:" + prepStmtHandle); @@ -687,7 +669,8 @@ void sendParamsByRPC(TDSWriter tdsWriter, private void buildServerCursorPrepExecParams(TDSWriter tdsWriter) throws SQLServerException { if (getStatementLogger().isLoggable(java.util.logging.Level.FINE)) - getStatementLogger().fine(toString() + ": calling sp_cursorprepexec: PreparedHandle:" + getPreparedStatementHandle() + ", SQL:" + preparedSQL); + getStatementLogger() + .fine(toString() + ": calling sp_cursorprepexec: PreparedHandle:" + getPreparedStatementHandle() + ", SQL:" + preparedSQL); expectPrepStmtHandle = true; executedSqlDirectly = false; @@ -717,8 +700,8 @@ private void buildServerCursorPrepExecParams(TDSWriter tdsWriter) throws SQLServ // IN // Note: we must strip out SCROLLOPT_PARAMETERIZED_STMT if we don't // actually have any parameters. - tdsWriter.writeRPCInt(null, - getResultSetScrollOpt() & ~((0 == preparedTypeDefinitions.length()) ? TDS.SCROLLOPT_PARAMETERIZED_STMT : 0), false); + tdsWriter.writeRPCInt(null, getResultSetScrollOpt() & ~((0 == preparedTypeDefinitions.length()) ? TDS.SCROLLOPT_PARAMETERIZED_STMT : 0), + false); // IN tdsWriter.writeRPCInt(null, getResultSetCCOpt(), false); @@ -775,13 +758,14 @@ private void buildExecSQLParams(TDSWriter tdsWriter) throws SQLServerException { tdsWriter.writeRPCStringUnicode(preparedSQL); // IN - if (preparedTypeDefinitions.length() > 0) + if (preparedTypeDefinitions.length() > 0) tdsWriter.writeRPCStringUnicode(preparedTypeDefinitions); } private void buildServerCursorExecParams(TDSWriter tdsWriter) throws SQLServerException { if (getStatementLogger().isLoggable(java.util.logging.Level.FINE)) - getStatementLogger().fine(toString() + ": calling sp_cursorexecute: PreparedHandle:" + getPreparedStatementHandle() + ", SQL:" + preparedSQL); + getStatementLogger() + .fine(toString() + ": calling sp_cursorexecute: PreparedHandle:" + getPreparedStatementHandle() + ", SQL:" + preparedSQL); expectPrepStmtHandle = false; executedSqlDirectly = false; @@ -1045,7 +1029,8 @@ else if (needsPrepare) return needsPrepare; } - /* L0 */ public final java.sql.ResultSetMetaData getMetaData() throws SQLServerException { + @Override + public final java.sql.ResultSetMetaData getMetaData() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getMetaData"); checkClosed(); boolean rsclosed = false; @@ -1077,7 +1062,7 @@ else if (resultSet != null) { * @throws SQLServerException * @return the result set containing the meta data */ - /* L0 */ private ResultSet buildExecuteMetaData() throws SQLServerException { + private ResultSet buildExecuteMetaData() throws SQLServerException { String fmtSQL = userSQL; ResultSet emptyResultSet = null; @@ -1109,7 +1094,7 @@ else if (resultSet != null) { * @exception SQLServerException * The index specified was outside the number of paramters for the statement. */ - /* L0 */ final Parameter setterGetParam(int index) throws SQLServerException { + final Parameter setterGetParam(int index) throws SQLServerException { if (index < 1 || index > inOutParam.length) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_indexOutOfRange")); Object[] msgArgs = {index}; @@ -1174,6 +1159,7 @@ final void setSQLXMLInternal(int parameterIndex, stmtColumnEncriptionSetting, parameterIndex, userSQL, null); } + @Override public final void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1183,6 +1169,7 @@ public final void setAsciiStream(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setAsciiStream"); } + @Override public final void setAsciiStream(int n, java.io.InputStream x, int length) throws SQLServerException { @@ -1193,6 +1180,7 @@ public final void setAsciiStream(int n, loggerExternal.exiting(getClassNameLogging(), "setAsciiStream"); } + @Override public final void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { @@ -1203,93 +1191,42 @@ public final void setAsciiStream(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setAsciiStream"); } - private Parameter getParam(int index) throws SQLServerException { - index--; - if (index < 0 || index >= inOutParam.length) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_indexOutOfRange")); - Object[] msgArgs = {index + 1}; - SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), "07009", false); - } - return inOutParam[index]; - } - - public final void setBigDecimal(int n, + @Override + public final void setBigDecimal(int paramterIndex, BigDecimal x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {n, x}); + loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {paramterIndex, x}); checkClosed(); - setValue(n, JDBCType.DECIMAL, x, JavaType.BIGDECIMAL, false); + setValue(paramterIndex, JDBCType.DECIMAL, x, JavaType.BIGDECIMAL, false); loggerExternal.exiting(getClassNameLogging(), "setBigDecimal"); } - /** - * Sets the designated parameter to the given java.math.BigDecimal value. The driver converts this to an SQL NUMERIC - * value when it sends it to the database. - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param precision - * the precision of the column - * @param scale - * the scale of the column - * @throws SQLServerException - * when an error occurs - */ - public final void setBigDecimal(int n, + @Override + public final void setBigDecimal(int paramterIndex, BigDecimal x, int precision, int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {n, x, precision, scale}); + loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {paramterIndex, x, precision, scale}); checkClosed(); - setValue(n, JDBCType.DECIMAL, x, JavaType.BIGDECIMAL, precision, scale, false); + setValue(paramterIndex, JDBCType.DECIMAL, x, JavaType.BIGDECIMAL, precision, scale, false); loggerExternal.exiting(getClassNameLogging(), "setBigDecimal"); } - /** - * Sets the designated parameter to the given java.math.BigDecimal value. The driver converts this to an SQL NUMERIC - * value when it sends it to the database. - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param precision - * the precision of the column - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ - public final void setBigDecimal(int n, + @Override + public final void setBigDecimal(int paramterIndex, BigDecimal x, int precision, int scale, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {n, x, precision, scale, forceEncrypt}); + loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {paramterIndex, x, precision, scale, forceEncrypt}); checkClosed(); - setValue(n, JDBCType.DECIMAL, x, JavaType.BIGDECIMAL, precision, scale, forceEncrypt); + setValue(paramterIndex, JDBCType.DECIMAL, x, JavaType.BIGDECIMAL, precision, scale, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setBigDecimal"); } - /** - * Sets the designated parameter to the given java.math.BigDecimal value. The driver converts this to an SQL NUMERIC - * value when it sends it to the database. - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setMoney(int n, BigDecimal x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1299,21 +1236,7 @@ public final void setMoney(int n, loggerExternal.exiting(getClassNameLogging(), "setMoney"); } - /** - * Sets the designated parameter to the given java.math.BigDecimal value. The driver converts this to an SQL NUMERIC - * value when it sends it to the database. - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setMoney(int n, BigDecimal x, boolean forceEncrypt) throws SQLServerException { @@ -1324,17 +1247,7 @@ public final void setMoney(int n, loggerExternal.exiting(getClassNameLogging(), "setMoney"); } - /** - * Sets the designated parameter to the given java.math.BigDecimal value. The driver converts this to an SQL NUMERIC - * value when it sends it to the database. - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setSmallMoney(int n, BigDecimal x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1344,21 +1257,7 @@ public final void setSmallMoney(int n, loggerExternal.exiting(getClassNameLogging(), "setSmallMoney"); } - /** - * Sets the designated parameter to the given java.math.BigDecimal value. The driver converts this to an SQL NUMERIC - * value when it sends it to the database. - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setSmallMoney(int n, BigDecimal x, boolean forceEncrypt) throws SQLServerException { @@ -1369,6 +1268,7 @@ public final void setSmallMoney(int n, loggerExternal.exiting(getClassNameLogging(), "setSmallMoney"); } + @Override public final void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1378,6 +1278,7 @@ public final void setBinaryStream(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setBinaryStream"); } + @Override public final void setBinaryStream(int n, java.io.InputStream x, int length) throws SQLServerException { @@ -1388,6 +1289,7 @@ public final void setBinaryStream(int n, loggerExternal.exiting(getClassNameLogging(), "setBinaryStream"); } + @Override public final void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { @@ -1398,6 +1300,7 @@ public final void setBinaryStream(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setBinaryStream"); } + @Override public final void setBoolean(int n, boolean x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1407,21 +1310,7 @@ public final void setBoolean(int n, loggerExternal.exiting(getClassNameLogging(), "setBoolean"); } - /** - * Sets the designated parameter to the given Java boolean value. The driver converts this to an SQL BIT or - * BOOLEAN value when it sends it to the database. - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setBoolean(int n, boolean x, boolean forceEncrypt) throws SQLServerException { @@ -1432,6 +1321,7 @@ public final void setBoolean(int n, loggerExternal.exiting(getClassNameLogging(), "setBoolean"); } + @Override public final void setByte(int n, byte x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1441,21 +1331,7 @@ public final void setByte(int n, loggerExternal.exiting(getClassNameLogging(), "setByte"); } - /** - * Sets the designated parameter to the given Java byte value. The driver converts this to an SQL TINYINT value when it - * sends it to the database. - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setByte(int n, byte x, boolean forceEncrypt) throws SQLServerException { @@ -1466,6 +1342,7 @@ public final void setByte(int n, loggerExternal.exiting(getClassNameLogging(), "setByte"); } + @Override public final void setBytes(int n, byte x[]) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1475,22 +1352,7 @@ public final void setBytes(int n, loggerExternal.exiting(getClassNameLogging(), "setBytes"); } - /** - * Sets the designated parameter to the given Java array of bytes. The driver converts this to an SQL VARBINARY or - * LONGVARBINARY (depending on the argument's size relative to the driver's limits on VARBINARY values) when it sends it - * to the database. - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setBytes(int n, byte x[], boolean forceEncrypt) throws SQLServerException { @@ -1501,16 +1363,7 @@ public final void setBytes(int n, loggerExternal.exiting(getClassNameLogging(), "setBytes"); } - /** - * Sets the designated parameter to the given String. The driver converts this to an SQL GUID - * - * @param index - * the first parameter is 1, the second is 2, ... - * @param guid - * string representation of the uniqueIdentifier value - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setUniqueIdentifier(int index, String guid) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1520,20 +1373,7 @@ public final void setUniqueIdentifier(int index, loggerExternal.exiting(getClassNameLogging(), "setUniqueIdentifier"); } - /** - * Sets the designated parameter to the given String. The driver converts this to an SQL GUID - * - * @param index - * the first parameter is 1, the second is 2, ... - * @param guid - * string representation of the uniqueIdentifier value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setUniqueIdentifier(int index, String guid, boolean forceEncrypt) throws SQLServerException { @@ -1544,6 +1384,7 @@ public final void setUniqueIdentifier(int index, loggerExternal.exiting(getClassNameLogging(), "setUniqueIdentifier"); } + @Override public final void setDouble(int n, double x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1553,21 +1394,7 @@ public final void setDouble(int n, loggerExternal.exiting(getClassNameLogging(), "setDouble"); } - /** - * Sets the designated parameter to the given Java double value. The driver converts this to an SQL DOUBLE value when it - * sends it to the database. - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setDouble(int n, double x, boolean forceEncrypt) throws SQLServerException { @@ -1578,6 +1405,7 @@ public final void setDouble(int n, loggerExternal.exiting(getClassNameLogging(), "setDouble"); } + @Override public final void setFloat(int n, float x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1587,21 +1415,7 @@ public final void setFloat(int n, loggerExternal.exiting(getClassNameLogging(), "setFloat"); } - /** - * Sets the designated parameter to the given Java float value. The driver converts this to an SQL REAL value when it - * sends it to the database. - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setFloat(int n, float x, boolean forceEncrypt) throws SQLServerException { @@ -1611,7 +1425,8 @@ public final void setFloat(int n, setValue(n, JDBCType.REAL, x, JavaType.FLOAT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setFloat"); } - + + @Override public final void setGeometry(int n, Geometry x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1620,7 +1435,8 @@ public final void setGeometry(int n, setValue(n, JDBCType.GEOMETRY, x, JavaType.STRING, false); loggerExternal.exiting(getClassNameLogging(), "setGeometry"); } - + + @Override public final void setGeography(int n, Geography x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1630,6 +1446,7 @@ public final void setGeography(int n, loggerExternal.exiting(getClassNameLogging(), "setGeography"); } + @Override public final void setInt(int n, int value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1639,21 +1456,7 @@ public final void setInt(int n, loggerExternal.exiting(getClassNameLogging(), "setInt"); } - /** - * Sets the designated parameter to the given Java int value. The driver converts this to an SQL INTEGER value when it - * sends it to the database. - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param value - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setInt(int n, int value, boolean forceEncrypt) throws SQLServerException { @@ -1664,6 +1467,7 @@ public final void setInt(int n, loggerExternal.exiting(getClassNameLogging(), "setInt"); } + @Override public final void setLong(int n, long x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1673,21 +1477,7 @@ public final void setLong(int n, loggerExternal.exiting(getClassNameLogging(), "setLong"); } - /** - * Sets the designated parameter to the given Java long value. The driver converts this to an SQL BIGINT value when it - * sends it to the database. - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setLong(int n, long x, boolean forceEncrypt) throws SQLServerException { @@ -1698,6 +1488,7 @@ public final void setLong(int n, loggerExternal.exiting(getClassNameLogging(), "setLong"); } + @Override public final void setNull(int index, int jdbcType) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1746,6 +1537,7 @@ final void setObjectNoType(int index, } } + @Override public final void setObject(int index, Object obj) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1755,6 +1547,7 @@ public final void setObject(int index, loggerExternal.exiting(getClassNameLogging(), "setObject"); } + @Override public final void setObject(int n, Object obj, int jdbcType) throws SQLServerException { @@ -1768,6 +1561,7 @@ public final void setObject(int n, loggerExternal.exiting(getClassNameLogging(), "setObject"); } + @Override public final void setObject(int parameterIndex, Object x, int targetSqlType, @@ -1790,34 +1584,7 @@ public final void setObject(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setObject"); } - /** - *

- * Sets the value of the designated parameter with the given object. - * - *

- * The given Java object will be converted to the given targetSqlType before being sent to the database. - * - * If the object has a custom mapping (is of a class implementing the interface SQLData), the JDBC driver should call the method - * SQLData.writeSQL to write it to the SQL data stream. If, on the other hand, the object is of a class implementing - * Ref, Blob, Clob, NClob, Struct, java.net.URL, or - * Array, the driver should pass it to the database as a value of the corresponding SQL type. - * - *

- * Note that this method may be used to pass database-specific abstract data types. - * - * @param parameterIndex - * the first parameter is 1, the second is 2, ... - * @param x - * the object containing the input parameter value - * @param targetSqlType - * the SQL type (as defined in java.sql.Types) to be sent to the database. The scale argument may further qualify this type. - * @param precision - * the precision of the column - * @param scale - * scale of the column - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setObject(int parameterIndex, Object x, int targetSqlType, @@ -1832,46 +1599,15 @@ public final void setObject(int parameterIndex, // InputStream and Reader, this is the length of the data in the stream or reader. // For all other types, this value will be ignored. - setObject(setterGetParam(parameterIndex), x, JavaType.of(x), - JDBCType.of(targetSqlType), (java.sql.Types.NUMERIC == targetSqlType || java.sql.Types.DECIMAL == targetSqlType - || InputStream.class.isInstance(x) || Reader.class.isInstance(x)) ? scale : null, + setObject( + setterGetParam(parameterIndex), x, JavaType.of(x), JDBCType.of(targetSqlType), (java.sql.Types.NUMERIC == targetSqlType + || java.sql.Types.DECIMAL == targetSqlType || InputStream.class.isInstance(x) || Reader.class.isInstance(x)) ? scale : null, precision, false, parameterIndex, null); loggerExternal.exiting(getClassNameLogging(), "setObject"); } - /** - *

- * Sets the value of the designated parameter with the given object. - * - *

- * The given Java object will be converted to the given targetSqlType before being sent to the database. - * - * If the object has a custom mapping (is of a class implementing the interface SQLData), the JDBC driver should call the method - * SQLData.writeSQL to write it to the SQL data stream. If, on the other hand, the object is of a class implementing - * Ref, Blob, Clob, NClob, Struct, java.net.URL, or - * Array, the driver should pass it to the database as a value of the corresponding SQL type. - * - *

- * Note that this method may be used to pass database-specific abstract data types. - * - * @param parameterIndex - * the first parameter is 1, the second is 2, ... - * @param x - * the object containing the input parameter value - * @param targetSqlType - * the SQL type (as defined in java.sql.Types) to be sent to the database. The scale argument may further qualify this type. - * @param precision - * the precision of the column - * @param scale - * scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setObject(int parameterIndex, Object x, int targetSqlType, @@ -1888,9 +1624,9 @@ public final void setObject(int parameterIndex, // InputStream and Reader, this is the length of the data in the stream or reader. // For all other types, this value will be ignored. - setObject(setterGetParam(parameterIndex), x, JavaType.of(x), - JDBCType.of(targetSqlType), (java.sql.Types.NUMERIC == targetSqlType || java.sql.Types.DECIMAL == targetSqlType - || InputStream.class.isInstance(x) || Reader.class.isInstance(x)) ? scale : null, + setObject( + setterGetParam(parameterIndex), x, JavaType.of(x), JDBCType.of(targetSqlType), (java.sql.Types.NUMERIC == targetSqlType + || java.sql.Types.DECIMAL == targetSqlType || InputStream.class.isInstance(x) || Reader.class.isInstance(x)) ? scale : null, precision, forceEncrypt, parameterIndex, null); loggerExternal.exiting(getClassNameLogging(), "setObject"); @@ -1956,6 +1692,41 @@ final void setObject(Parameter param, } } + @Override + public final void setObject(int index, + Object obj, + SQLType jdbcType) throws SQLServerException { + setObject(index, obj, jdbcType.getVendorTypeNumber()); + } + + @Override + public final void setObject(int parameterIndex, + Object x, + SQLType targetSqlType, + int scaleOrLength) throws SQLServerException { + setObject(parameterIndex, x, targetSqlType.getVendorTypeNumber(), scaleOrLength); + } + + @Override + public final void setObject(int parameterIndex, + Object x, + SQLType targetSqlType, + Integer precision, + Integer scale) throws SQLServerException { + setObject(parameterIndex, x, targetSqlType.getVendorTypeNumber(), precision, scale); + } + + @Override + public final void setObject(int parameterIndex, + Object x, + SQLType targetSqlType, + Integer precision, + Integer scale, + boolean forceEncrypt) throws SQLServerException { + setObject(parameterIndex, x, targetSqlType.getVendorTypeNumber(), precision, scale, forceEncrypt); + } + + @Override public final void setShort(int index, short x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1965,21 +1736,7 @@ public final void setShort(int index, loggerExternal.exiting(getClassNameLogging(), "setShort"); } - /** - * Sets the designated parameter to the given Java short value. The driver converts this to an SQL SMALLINT value when - * it sends it to the database. - * - * @param index - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setShort(int index, short x, boolean forceEncrypt) throws SQLServerException { @@ -1990,6 +1747,7 @@ public final void setShort(int index, loggerExternal.exiting(getClassNameLogging(), "setShort"); } + @Override public final void setString(int index, String str) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1999,22 +1757,7 @@ public final void setString(int index, loggerExternal.exiting(getClassNameLogging(), "setString"); } - /** - * Sets the designated parameter to the given Java String value. The driver converts this to an SQL VARCHAR or - * LONGVARCHAR value (depending on the argument's size relative to the driver's limits on VARCHAR values) when it sends - * it to the database. - * - * @param index - * the first parameter is 1, the second is 2, ... - * @param str - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setString(int index, String str, boolean forceEncrypt) throws SQLServerException { @@ -2025,6 +1768,7 @@ public final void setString(int index, loggerExternal.exiting(getClassNameLogging(), "setString"); } + @Override public final void setNString(int parameterIndex, String value) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2034,25 +1778,10 @@ public final void setNString(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setNString"); } - /** - * Sets the designated parameter to the given String object. The driver converts this to a SQL NCHAR or - * NVARCHAR or LONGNVARCHAR value (depending on the argument's size relative to the driver's limits on - * NVARCHAR values) when it sends it to the database. - * - * @param parameterIndex - * of the first parameter is 1, the second is 2, ... - * @param value - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLException - * when an error occurs - */ + @Override public final void setNString(int parameterIndex, String value, - boolean forceEncrypt) throws SQLException { + boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setNString", new Object[] {parameterIndex, value, forceEncrypt}); checkClosed(); @@ -2060,6 +1789,7 @@ public final void setNString(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setNString"); } + @Override public final void setTime(int n, java.sql.Time x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2069,18 +1799,7 @@ public final void setTime(int n, loggerExternal.exiting(getClassNameLogging(), "setTime"); } - /** - * Sets the designated parameter to the given java.sql.Time value - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param scale - * the scale of the column - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setTime(int n, java.sql.Time x, int scale) throws SQLServerException { @@ -2091,22 +1810,7 @@ public final void setTime(int n, loggerExternal.exiting(getClassNameLogging(), "setTime"); } - /** - * Sets the designated parameter to the given java.sql.Time value - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setTime(int n, java.sql.Time x, int scale, @@ -2118,6 +1822,7 @@ public final void setTime(int n, loggerExternal.exiting(getClassNameLogging(), "setTime"); } + @Override public final void setTimestamp(int n, java.sql.Timestamp x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2127,18 +1832,7 @@ public final void setTimestamp(int n, loggerExternal.exiting(getClassNameLogging(), "setTimestamp"); } - /** - * Sets the designated parameter to the given java.sql.Timestamp value - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param scale - * the scale of the column - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setTimestamp(int n, java.sql.Timestamp x, int scale) throws SQLServerException { @@ -2149,22 +1843,7 @@ public final void setTimestamp(int n, loggerExternal.exiting(getClassNameLogging(), "setTimestamp"); } - /** - * Sets the designated parameter to the given java.sql.Timestamp value - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setTimestamp(int n, java.sql.Timestamp x, int scale, @@ -2176,18 +1855,9 @@ public final void setTimestamp(int n, loggerExternal.exiting(getClassNameLogging(), "setTimestamp"); } - /** - * Sets the designated parameter to the given microsoft.sql.DatetimeOffset value - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @throws SQLException - * if an error occurs. - */ + @Override public final void setDateTimeOffset(int n, - microsoft.sql.DateTimeOffset x) throws SQLException { + microsoft.sql.DateTimeOffset x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {n, x}); checkClosed(); @@ -2195,21 +1865,10 @@ public final void setDateTimeOffset(int n, loggerExternal.exiting(getClassNameLogging(), "setDateTimeOffset"); } - /** - * Sets the designated parameter to the given microsoft.sql.DatetimeOffset value - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param scale - * the scale of the column - * @throws SQLException - * when an error occurs - */ + @Override public final void setDateTimeOffset(int n, microsoft.sql.DateTimeOffset x, - int scale) throws SQLException { + int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {n, x, scale}); checkClosed(); @@ -2217,26 +1876,11 @@ public final void setDateTimeOffset(int n, loggerExternal.exiting(getClassNameLogging(), "setDateTimeOffset"); } - /** - * Sets the designated parameter to the given microsoft.sql.DatetimeOffset value - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLException - * when an error occurs - */ + @Override public final void setDateTimeOffset(int n, microsoft.sql.DateTimeOffset x, int scale, - boolean forceEncrypt) throws SQLException { + boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {n, x, scale, forceEncrypt}); checkClosed(); @@ -2244,6 +1888,7 @@ public final void setDateTimeOffset(int n, loggerExternal.exiting(getClassNameLogging(), "setDateTimeOffset"); } + @Override public final void setDate(int n, java.sql.Date x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2253,16 +1898,7 @@ public final void setDate(int n, loggerExternal.exiting(getClassNameLogging(), "setDate"); } - /** - * Sets the designated parameter to the given java.sql.Timestamp value - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setDateTime(int n, java.sql.Timestamp x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2272,20 +1908,7 @@ public final void setDateTime(int n, loggerExternal.exiting(getClassNameLogging(), "setDateTime"); } - /** - * Sets the designated parameter to the given java.sql.Timestamp value - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setDateTime(int n, java.sql.Timestamp x, boolean forceEncrypt) throws SQLServerException { @@ -2296,16 +1919,7 @@ public final void setDateTime(int n, loggerExternal.exiting(getClassNameLogging(), "setDateTime"); } - /** - * Sets the designated parameter to the given java.sql.Timestamp value - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setSmallDateTime(int n, java.sql.Timestamp x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2315,20 +1929,7 @@ public final void setSmallDateTime(int n, loggerExternal.exiting(getClassNameLogging(), "setSmallDateTime"); } - /** - * Sets the designated parameter to the given java.sql.Timestamp value - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setSmallDateTime(int n, java.sql.Timestamp x, boolean forceEncrypt) throws SQLServerException { @@ -2339,18 +1940,7 @@ public final void setSmallDateTime(int n, loggerExternal.exiting(getClassNameLogging(), "setSmallDateTime"); } - /** - * Populates a table valued parameter with a data table - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param tvpName - * the name of the table valued parameter - * @param tvpDataTable - * the source datatable object - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setStructured(int n, String tvpName, SQLServerDataTable tvpDataTable) throws SQLServerException { @@ -2362,18 +1952,7 @@ public final void setStructured(int n, loggerExternal.exiting(getClassNameLogging(), "setStructured"); } - /** - * Populates a table valued parameter with a data table - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param tvpName - * the name of the table valued parameter - * @param tvpResultSet - * the source resultset object - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setStructured(int n, String tvpName, ResultSet tvpResultSet) throws SQLServerException { @@ -2385,18 +1964,7 @@ public final void setStructured(int n, loggerExternal.exiting(getClassNameLogging(), "setStructured"); } - /** - * Populates a table valued parameter with a data table - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param tvpName - * the name of the table valued parameter - * @param tvpBulkRecord - * an ISQLServerDataRecord object - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setStructured(int n, String tvpName, ISQLServerDataRecord tvpBulkRecord) throws SQLServerException { @@ -2412,16 +1980,16 @@ String getTVPNameIfNull(int n, String tvpName) throws SQLServerException { if ((null == tvpName) || (0 == tvpName.length())) { // Check if the CallableStatement/PreparedStatement is a stored procedure call - if(null != this.procedureName) { + if (null != this.procedureName) { SQLServerParameterMetaData pmd = (SQLServerParameterMetaData) this.getParameterMetaData(); pmd.isTVP = true; - + if (!pmd.procedureIsFound) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_StoredProcedureNotFound")); Object[] msgArgs = {this.procedureName}; SQLServerException.makeFromDriverError(connection, pmd, form.format(msgArgs), null, false); } - + try { String tvpNameWithoutSchema = pmd.getParameterTypeName(n); String tvpSchema = pmd.getTVPSchemaFromStoredProcedure(n); @@ -2442,12 +2010,14 @@ String getTVPNameIfNull(int n, } @Deprecated + @Override public final void setUnicodeStream(int n, java.io.InputStream x, int length) throws SQLException { - NotImplemented(); + SQLServerException.throwNotSupportedException(connection, this); } + @Override public final void addBatch() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "addBatch"); checkClosed(); @@ -2464,13 +2034,15 @@ public final void addBatch() throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "addBatch"); } - /* L0 */ public final void clearBatch() throws SQLServerException { + @Override + public final void clearBatch() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "clearBatch"); checkClosed(); batchParamValues = null; loggerExternal.exiting(getClassNameLogging(), "clearBatch"); } + @Override public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQLTimeoutException { loggerExternal.entering(getClassNameLogging(), "executeBatch"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -2615,9 +2187,8 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL return updateCounts; } + @Override public long[] executeLargeBatch() throws SQLServerException, BatchUpdateException, SQLTimeoutException { - DriverJDBCVersion.checkSupportsJDBC42(); - loggerExternal.entering(getClassNameLogging(), "executeLargeBatch"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); @@ -3074,7 +2645,7 @@ private final class PrepStmtBatchExecCmd extends TDSCommand { long updateCounts[]; PrepStmtBatchExecCmd(SQLServerPreparedStatement stmt) { - super(stmt.toString() + " executeBatch", queryTimeout, cancelQueryTimeoutSeconds); + super(stmt.toString() + " executeBatch", queryTimeout, cancelQueryTimeoutSeconds); this.stmt = stmt; } @@ -3115,23 +2686,18 @@ final void doExecutePreparedStatementBatch(PrepStmtBatchExecCmd batchCommand) th // Create the parameter array that we'll use for all the items in this batch. Parameter[] batchParam = new Parameter[inOutParam.length]; - -/* - TDSWriter tdsWriter = null; - while (numBatchesExecuted < numBatches) { - // Fill in the parameter values for this batch - Parameter paramValues[] = batchParamValues.get(numBatchesPrepared); - assert paramValues.length == batchParam.length; - System.arraycopy(paramValues, 0, batchParam, 0, paramValues.length); - - boolean hasExistingTypeDefinitions = preparedTypeDefinitions != null; - boolean hasNewTypeDefinitions = buildPreparedStrings(batchParam, false); - - // Get the encryption metadata for the first batch only. - if ((0 == numBatchesExecuted) && (Util.shouldHonorAEForParameters(stmtColumnEncriptionSetting, connection)) && (0 < batchParam.length) - && !isInternalEncryptionQuery && !encryptionMetadataIsRetrieved) { - getParameterEncryptionMetadata(batchParam); -*/ + /* + * TDSWriter tdsWriter = null; while (numBatchesExecuted < numBatches) { // Fill in the parameter values for this batch Parameter + * paramValues[] = batchParamValues.get(numBatchesPrepared); assert paramValues.length == batchParam.length; System.arraycopy(paramValues, 0, + * batchParam, 0, paramValues.length); + * + * boolean hasExistingTypeDefinitions = preparedTypeDefinitions != null; boolean hasNewTypeDefinitions = buildPreparedStrings(batchParam, + * false); + * + * // Get the encryption metadata for the first batch only. if ((0 == numBatchesExecuted) && + * (Util.shouldHonorAEForParameters(stmtColumnEncriptionSetting, connection)) && (0 < batchParam.length) && !isInternalEncryptionQuery && + * !encryptionMetadataIsRetrieved) { getParameterEncryptionMetadata(batchParam); + */ TDSWriter tdsWriter = null; while (numBatchesExecuted < numBatches) { @@ -3194,7 +2760,7 @@ final void doExecutePreparedStatementBatch(PrepStmtBatchExecCmd batchCommand) th // that repreparation is necessary. ++numBatchesPrepared; needsPrepare = doPrepExec(tdsWriter, batchParam, hasNewTypeDefinitions, hasExistingTypeDefinitions); - if ( needsPrepare || numBatchesPrepared == numBatches) { + if (needsPrepare || numBatchesPrepared == numBatches) { ensureExecuteResultsReader(batchCommand.startResponse(getIsResponseBufferingAdaptive())); boolean retry = false; @@ -3239,8 +2805,8 @@ final void doExecutePreparedStatementBatch(PrepStmtBatchExecCmd batchCommand) th // so just record the failure for the particular batch item. updateCount = Statement.EXECUTE_FAILED; if (null == batchCommand.batchException) - batchCommand.batchException = e; - + batchCommand.batchException = e; + } // In batch execution, we have a special update count @@ -3279,6 +2845,7 @@ else if (null != batchCommand.batchException) { } } + @Override public final void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3288,6 +2855,7 @@ public final void setCharacterStream(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setCharacterStream"); } + @Override public final void setCharacterStream(int n, java.io.Reader reader, int length) throws SQLServerException { @@ -3298,6 +2866,7 @@ public final void setCharacterStream(int n, loggerExternal.exiting(getClassNameLogging(), "setCharacterStream"); } + @Override public final void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { @@ -3308,6 +2877,7 @@ public final void setCharacterStream(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setCharacterStream"); } + @Override public final void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3317,6 +2887,7 @@ public final void setNCharacterStream(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setNCharacterStream"); } + @Override public final void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { @@ -3327,11 +2898,13 @@ public final void setNCharacterStream(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setNCharacterStream"); } - /* L0 */ public final void setRef(int i, - java.sql.Ref x) throws SQLServerException { - NotImplemented(); + @Override + public final void setRef(int i, + java.sql.Ref x) throws SQLException { + SQLServerException.throwNotSupportedException(connection, this); } + @Override public final void setBlob(int i, java.sql.Blob x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3341,6 +2914,7 @@ public final void setBlob(int i, loggerExternal.exiting(getClassNameLogging(), "setBlob"); } + @Override public final void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3350,6 +2924,7 @@ public final void setBlob(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setBlob"); } + @Override public final void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { @@ -3360,6 +2935,7 @@ public final void setBlob(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setBlob"); } + @Override public final void setClob(int parameterIndex, java.sql.Clob clobValue) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3369,6 +2945,7 @@ public final void setClob(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setClob"); } + @Override public final void setClob(int parameterIndex, Reader reader) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3378,6 +2955,7 @@ public final void setClob(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setClob"); } + @Override public final void setClob(int parameterIndex, Reader reader, long length) throws SQLException { @@ -3388,6 +2966,7 @@ public final void setClob(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setClob"); } + @Override public final void setNClob(int parameterIndex, NClob value) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3397,6 +2976,7 @@ public final void setNClob(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setNClob"); } + @Override public final void setNClob(int parameterIndex, Reader reader) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3406,6 +2986,7 @@ public final void setNClob(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setNClob"); } + @Override public final void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { @@ -3416,11 +2997,13 @@ public final void setNClob(int parameterIndex, loggerExternal.exiting(getClassNameLogging(), "setNClob"); } - /* L0 */ public final void setArray(int i, - java.sql.Array x) throws SQLServerException { - NotImplemented(); + @Override + public final void setArray(int i, + java.sql.Array x) throws SQLException { + SQLServerException.throwNotSupportedException(connection, this); } + @Override public final void setDate(int n, java.sql.Date x, java.util.Calendar cal) throws SQLServerException { @@ -3431,25 +3014,7 @@ public final void setDate(int n, loggerExternal.exiting(getClassNameLogging(), "setDate"); } - /** - * Sets the designated parameter to the given java.sql.Date value, using the given Calendar object. The driver uses the - * Calendar object to construct an SQL DATE value, which the driver then sends to the database. With a - * Calendar object, the driver can calculate the date taking into account a custom timezone. If no Calendar object is - * specified, the driver uses the default timezone, which is that of the virtual machine running the application. - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param cal - * the Calendar object the driver will use to construct the date - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setDate(int n, java.sql.Date x, java.util.Calendar cal, @@ -3461,6 +3026,7 @@ public final void setDate(int n, loggerExternal.exiting(getClassNameLogging(), "setDate"); } + @Override public final void setTime(int n, java.sql.Time x, java.util.Calendar cal) throws SQLServerException { @@ -3471,23 +3037,7 @@ public final void setTime(int n, loggerExternal.exiting(getClassNameLogging(), "setTime"); } - /** - * Sets the designated parameter to the given java.sql.Time value. The driver converts this to an SQL TIME value when it - * sends it to the database. - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param cal - * the Calendar object the driver will use to construct the date - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setTime(int n, java.sql.Time x, java.util.Calendar cal, @@ -3499,6 +3049,7 @@ public final void setTime(int n, loggerExternal.exiting(getClassNameLogging(), "setTime"); } + @Override public final void setTimestamp(int n, java.sql.Timestamp x, java.util.Calendar cal) throws SQLServerException { @@ -3509,23 +3060,7 @@ public final void setTimestamp(int n, loggerExternal.exiting(getClassNameLogging(), "setTimestamp"); } - /** - * Sets the designated parameter to the given java.sql.Timestamp value. The driver converts this to an SQL TIMESTAMP - * value when it sends it to the database. - * - * @param n - * the first parameter is 1, the second is 2, ... - * @param x - * the parameter value - * @param cal - * the Calendar object the driver will use to construct the date - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public final void setTimestamp(int n, java.sql.Timestamp x, java.util.Calendar cal, @@ -3537,6 +3072,7 @@ public final void setTimestamp(int n, loggerExternal.exiting(getClassNameLogging(), "setTimestamp"); } + @Override public final void setNull(int paramIndex, int sqlType, String typeName) throws SQLServerException { @@ -3552,17 +3088,7 @@ public final void setNull(int paramIndex, loggerExternal.exiting(getClassNameLogging(), "setNull"); } - /** - * Returns parameter metadata for the prepared statement. - * - * @param forceRefresh: - * If true the cache will not be used to retrieve the metadata. - * - * @return - * Per the description. - * - * @throws SQLServerException when an error occurs - */ + @Override public final ParameterMetaData getParameterMetaData(boolean forceRefresh) throws SQLServerException { SQLServerParameterMetaData pmd = this.connection.getCachedParameterMetadata(sqlTextCacheKey); @@ -3574,32 +3100,32 @@ public final ParameterMetaData getParameterMetaData(boolean forceRefresh) throws loggerExternal.entering(getClassNameLogging(), "getParameterMetaData"); checkClosed(); pmd = new SQLServerParameterMetaData(this, userSQL); - connection.registerCachedParameterMetadata(sqlTextCacheKey, pmd); - loggerExternal.exiting(getClassNameLogging(), "getParameterMetaData", pmd); - return pmd; } } /* JDBC 3.0 */ - /* L3 */ public final ParameterMetaData getParameterMetaData() throws SQLServerException { - return getParameterMetaData(false); + @Override + public final ParameterMetaData getParameterMetaData() throws SQLServerException { + return getParameterMetaData(false); } - /* L3 */ public final void setURL(int parameterIndex, - java.net.URL x) throws SQLServerException { - NotImplemented(); + @Override + public final void setURL(int parameterIndex, + java.net.URL x) throws SQLException { + SQLServerException.throwNotSupportedException(connection, this); } + @Override public final void setRowId(int parameterIndex, RowId x) throws SQLException { - // Not implemented - throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + SQLServerException.throwNotSupportedException(connection, this); } + @Override public final void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3610,28 +3136,32 @@ public final void setSQLXML(int parameterIndex, } /* make sure we throw here */ - /* L0 */ public final int executeUpdate(String sql) throws SQLServerException { + @Override + public final int executeUpdate(String sql) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "executeUpdate", sql); MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_cannotTakeArgumentsPreparedOrCallable")); Object[] msgArgs = {"executeUpdate()"}; throw new SQLServerException(this, form.format(msgArgs), null, 0, false); } - /* L0 */ public final boolean execute(String sql) throws SQLServerException { + @Override + public final boolean execute(String sql) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "execute", sql); MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_cannotTakeArgumentsPreparedOrCallable")); Object[] msgArgs = {"execute()"}; throw new SQLServerException(this, form.format(msgArgs), null, 0, false); } - /* L0 */ public final java.sql.ResultSet executeQuery(String sql) throws SQLServerException { + @Override + public final java.sql.ResultSet executeQuery(String sql) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "executeQuery", sql); MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_cannotTakeArgumentsPreparedOrCallable")); Object[] msgArgs = {"executeQuery()"}; throw new SQLServerException(this, form.format(msgArgs), null, 0, false); } - /* L0 */ public void addBatch(String sql) throws SQLServerException { + @Override + public void addBatch(String sql) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "addBatch", sql); MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_cannotTakeArgumentsPreparedOrCallable")); Object[] msgArgs = {"addBatch()"}; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement42.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement42.java deleted file mode 100644 index d0957cbb2..000000000 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement42.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.jdbc; - -import java.sql.SQLType; - -/** - * - * This class is separated from SQLServerPreparedStatement class in order to resolve compiling error of missing Java 8 Types when running with Java 7. - * - * This class will be initialized instead of SQLServerPreparedStatement when Java 8 and JDBC 4.2 are used. - * - * It shares the same PreparedStatement implementation with SQLServerCallableStatement42. - * - */ -public class SQLServerPreparedStatement42 extends SQLServerPreparedStatement implements ISQLServerPreparedStatement42 { - - SQLServerPreparedStatement42(SQLServerConnection conn, - String sql, - int nRSType, - int nRSConcur, - SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { - super(conn, sql, nRSType, nRSConcur, stmtColEncSetting); - } - - public final void setObject(int index, - Object obj, - SQLType jdbcType) throws SQLServerException { - SQLServerPreparedStatement42Helper.setObject(this, index, obj, jdbcType); - } - - public final void setObject(int parameterIndex, - Object x, - SQLType targetSqlType, - int scaleOrLength) throws SQLServerException { - SQLServerPreparedStatement42Helper.setObject(this, parameterIndex, x, targetSqlType, scaleOrLength); - } - - public final void setObject(int parameterIndex, - Object x, - SQLType targetSqlType, - Integer precision, - Integer scale) throws SQLServerException { - SQLServerPreparedStatement42Helper.setObject(this, parameterIndex, x, targetSqlType, precision, scale); - } - - public final void setObject(int parameterIndex, - Object x, - SQLType targetSqlType, - Integer precision, - Integer scale, - boolean forceEncrypt) throws SQLServerException { - SQLServerPreparedStatement42Helper.setObject(this, parameterIndex, x, targetSqlType, precision, scale, forceEncrypt); - } -} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement42Helper.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement42Helper.java deleted file mode 100644 index efd9c1774..000000000 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement42Helper.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.jdbc; - -import java.sql.SQLType; - -/** - * - * This class provides the underlying implementation of ISQLServerPreparedStatement42 interface to SQLServerPreparedStatement42 and - * SQLServerCallableStatement42, so that SQLServerPreparedStatement42 and SQLServerCallableStatement42 have the same implementation for same methods. - * - */ -class SQLServerPreparedStatement42Helper { - - static final void setObject(SQLServerPreparedStatement ps, - int index, - Object obj, - SQLType jdbcType) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (SQLServerStatement.loggerExternal.isLoggable(java.util.logging.Level.FINER)) - SQLServerStatement.loggerExternal.entering(ps.getClassNameLogging(), "setObject", new Object[] {index, obj, jdbcType}); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - ps.setObject(index, obj, jdbcType.getVendorTypeNumber()); - - SQLServerStatement.loggerExternal.exiting(ps.getClassNameLogging(), "setObject"); - } - - static final void setObject(SQLServerPreparedStatement ps, - int parameterIndex, - Object x, - SQLType targetSqlType, - int scaleOrLength) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (SQLServerStatement.loggerExternal.isLoggable(java.util.logging.Level.FINER)) - SQLServerStatement.loggerExternal.entering(ps.getClassNameLogging(), "setObject", - new Object[] {parameterIndex, x, targetSqlType, scaleOrLength}); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - ps.setObject(parameterIndex, x, targetSqlType.getVendorTypeNumber(), scaleOrLength); - - SQLServerStatement.loggerExternal.exiting(ps.getClassNameLogging(), "setObject"); - } - - static final void setObject(SQLServerPreparedStatement ps, - int parameterIndex, - Object x, - SQLType targetSqlType, - Integer precision, - Integer scale) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (SQLServerStatement.loggerExternal.isLoggable(java.util.logging.Level.FINER)) - SQLServerStatement.loggerExternal.entering(ps.getClassNameLogging(), "setObject", - new Object[] {parameterIndex, x, targetSqlType, precision, scale}); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - ps.setObject(parameterIndex, x, targetSqlType.getVendorTypeNumber(), precision, scale, false); - - SQLServerStatement.loggerExternal.exiting(ps.getClassNameLogging(), "setObject"); - } - - static final void setObject(SQLServerPreparedStatement ps, - int parameterIndex, - Object x, - SQLType targetSqlType, - Integer precision, - Integer scale, - boolean forceEncrypt) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (SQLServerStatement.loggerExternal.isLoggable(java.util.logging.Level.FINER)) - SQLServerStatement.loggerExternal.entering(ps.getClassNameLogging(), "setObject", - new Object[] {parameterIndex, x, targetSqlType, precision, scale, forceEncrypt}); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - ps.setObject(parameterIndex, x, targetSqlType.getVendorTypeNumber(), precision, scale, forceEncrypt); - - SQLServerStatement.loggerExternal.exiting(ps.getClassNameLogging(), "setObject"); - } -} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 3c05548af..afe0799bb 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -40,7 +40,6 @@ protected Object[][] getContents() { {"R_unknownJDBCType", "Invalid JDBC data type {0}."}, {"R_notSQLServer", "The driver received an unexpected pre-login response. Verify the connection properties and check that an instance of SQL Server is running on the host and accepting TCP/IP connections at the port. This driver can be used only with SQL Server 2005 or later."}, {"R_tcpOpenFailed", "{0}. Verify the connection properties. Make sure that an instance of SQL Server is running on the host and accepting TCP/IP connections at the port. Make sure that TCP connections to the port are not blocked by a firewall."}, - {"R_unsupportedJREVersion", "Java Runtime Environment (JRE) version {0} is not supported by this driver. Use the sqljdbc4.jar class library, which provides support for JDBC 4.0."}, {"R_unsupportedServerVersion", "SQL Server version {0} is not supported by this driver."}, {"R_noServerResponse", "SQL Server did not return a response. The connection has been closed."}, {"R_truncatedServerResponse", "SQL Server returned an incomplete response. The connection has been closed."}, diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java index 31709297a..b7b2370c9 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java @@ -22,7 +22,7 @@ import java.sql.ResultSetMetaData; import java.sql.RowId; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; +import java.sql.SQLType; import java.sql.SQLWarning; import java.sql.SQLXML; import java.text.MessageFormat; @@ -45,7 +45,12 @@ enum RowType { /** * Top-level JDBC ResultSet implementation */ -public class SQLServerResultSet implements ISQLServerResultSet { +public class SQLServerResultSet implements ISQLServerResultSet, java.io.Serializable { + + /** + * Always refresh SerialVersionUID when prompted + */ + private static final long serialVersionUID = -1624082547992040463L; /** Generate the statement's logging ID */ private static final AtomicInteger lastResultSetID = new AtomicInteger(0); @@ -57,6 +62,7 @@ private static int nextResultSetID() { final static java.util.logging.Logger logger = java.util.logging.Logger.getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerResultSet"); + @Override public String toString() { return traceID; } @@ -86,7 +92,7 @@ String getClassNameLogging() { private boolean isClosed = false; private final int serverCursorId; - + protected int getServerCursorId() { return serverCursorId; } @@ -208,23 +214,18 @@ private void skipColumns(int columnsToSkip, /** TDS reader from which row values are read */ private TDSReader tdsReader; - + protected TDSReader getTDSReader() { return tdsReader; } private final FetchBuffer fetchBuffer; - - /** - * Exposes Data Classification information for the current ResultSet For SQL Servers that do not support Data Classification or results that do - * not fetch any classified columns, this data can be null - * - * @return SensitivityClassification - */ + + @Override public SensitivityClassification getSensitivityClassification() { return tdsReader.sensitivityClassification; } - + /** * Make a new result set * @@ -406,6 +407,7 @@ boolean onDone(TDSReader tdsReader) throws SQLServerException { } } + @Override public boolean isWrapperFor(Class iface) throws SQLException { loggerExternal.entering(getClassNameLogging(), "isWrapperFor"); boolean f = iface.isInstance(this); @@ -413,6 +415,7 @@ public boolean isWrapperFor(Class iface) throws SQLException { return f; } + @Override public T unwrap(Class iface) throws SQLException { loggerExternal.entering(getClassNameLogging(), "unwrap"); T t; @@ -433,7 +436,7 @@ public T unwrap(Class iface) throws SQLException { * * @throws SQLServerException */ - /* L0 */ void checkClosed() throws SQLServerException { + void checkClosed() throws SQLServerException { if (isClosed) { SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_resultsetClosed"), null, false); @@ -448,6 +451,7 @@ public T unwrap(Class iface) throws SQLException { throw rowErrorException; } + @Override public boolean isClosed() throws SQLException { loggerExternal.entering(getClassNameLogging(), "isClosed"); boolean result = isClosed || stmt.isClosed(); @@ -458,10 +462,11 @@ public boolean isClosed() throws SQLException { /** * Called by ResultSet API methods to disallow method use on forward only result sets. * - * @throws SQLServerException + * @throws SQLException * if the result set is forward only. + * @throws SQLFeatureNotSupportedException */ - private void throwNotScrollable() throws SQLServerException { + private void throwNotScrollable() throws SQLException { SQLServerException.makeFromDriverError(stmt.connection, this, SQLServerException.getErrString("R_requestedOpNotSupportedOnForward"), null, true); } @@ -474,7 +479,7 @@ private boolean isDynamic() { return 0 != serverCursorId && TDS.SCROLLOPT_DYNAMIC == stmt.getCursorType(); } - private void verifyResultSetIsScrollable() throws SQLServerException { + private void verifyResultSetIsScrollable() throws SQLException { if (isForwardOnly()) throwNotScrollable(); } @@ -603,6 +608,7 @@ private void closeInternal() { stmt.decrResultSetCount(); } + @Override public void close() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "close"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -621,6 +627,7 @@ public void close() throws SQLServerException { * If any errors occur. * @return the column index */ + @Override public int findColumn(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "findColumn", columnName); checkClosed(); @@ -682,7 +689,7 @@ final Column getColumn(int columnIndex) throws SQLServerException { // before moving to another one. if (null != activeStream) { try { - fillLOBs(); + fillLOBs(); activeStream.close(); } catch (IOException e) { @@ -743,17 +750,14 @@ private Column loadColumn(int index) throws SQLServerException { return getColumn(index); } - /* L0 */ private void NotImplemented() throws SQLServerException { - SQLServerException.makeFromDriverError(stmt.connection, stmt, SQLServerException.getErrString("R_notSupported"), null, false); - } - /** * Clear result set warnings * * @throws SQLServerException * when an error occurs */ - /* L0 */ public void clearWarnings() throws SQLServerException { + @Override + public void clearWarnings() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "clearWarnings"); loggerExternal.exiting(getClassNameLogging(), "clearWarnings"); } @@ -761,12 +765,13 @@ private Column loadColumn(int index) throws SQLServerException { /* ----------------- JDBC API methods ------------------ */ private void moverInit() throws SQLServerException { - fillLOBs(); + fillLOBs(); cancelInsert(); cancelUpdates(); } - public boolean relative(int rows) throws SQLServerException { + @Override + public boolean relative(int rows) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "relative", rows); @@ -955,6 +960,7 @@ private void updateCurrentRow(int rowsToMove) { * * @return false when there are no more rows to read */ + @Override public boolean next() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "next"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -980,7 +986,7 @@ public boolean next() throws SQLServerException { moveFirst(); else moveForward(1); - + boolean value = hasCurrentRow(); loggerExternal.exiting(getClassNameLogging(), "next", value); return value; @@ -1047,6 +1053,7 @@ public boolean next() throws SQLServerException { return false; } + @Override public boolean wasNull() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "wasNull"); checkClosed(); @@ -1058,7 +1065,8 @@ public boolean wasNull() throws SQLServerException { /** * @return true if the cursor is before the first row in this result set, returns false otherwise or if the result set contains no rows. */ - public boolean isBeforeFirst() throws SQLServerException { + @Override + public boolean isBeforeFirst() throws SQLException { loggerExternal.entering(getClassNameLogging(), "isBeforeFirst"); if (logger.isLoggable(java.util.logging.Level.FINER)) logger.finer(toString() + logCursorState()); @@ -1117,7 +1125,8 @@ public boolean isBeforeFirst() throws SQLServerException { return value; } - public boolean isAfterLast() throws SQLServerException { + @Override + public boolean isAfterLast() throws SQLException { loggerExternal.entering(getClassNameLogging(), "isAfterLast"); if (logger.isLoggable(java.util.logging.Level.FINER)) logger.finer(toString() + logCursorState()); @@ -1152,14 +1161,24 @@ public boolean isAfterLast() throws SQLServerException { } /** - * Determines whether the cursor is on the first row in this ResultSet object. - * + * Retrieves whether the cursor is on the first row of this ResultSet object. + * * This method should be called only on ResultSet objects that are scrollable: TYPE_SCROLL_SENSITIVE, TYPE_SCROLL_INSENSITIVE, * TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC. + * + *

+ * Note:Support for the isFirst method is optional for ResultSets with a result set type of + * TYPE_FORWARD_ONLY * - * @return true if the cursor is on the first row in this result set + * @return true if the cursor is on the first row; false otherwise + * + * @exception SQLException + * if a database access error occurs or this method is called on a closed result set + * + * @since 1.2 */ - public boolean isFirst() throws SQLServerException { + @Override + public boolean isFirst() throws SQLException { loggerExternal.entering(getClassNameLogging(), "isFirst"); if (logger.isLoggable(java.util.logging.Level.FINER)) logger.finer(toString() + logCursorState()); @@ -1189,14 +1208,27 @@ public boolean isFirst() throws SQLServerException { } /** - * Determines whether the cursor is on the last row in this ResultSet object. - * + * Retrieves whether the cursor is on the last row of this ResultSet object. Note: Calling the method + * isLast may be expensive because the JDBC driver might need to fetch ahead one row in order to determine whether the current row is + * the last row in the result set. + * + *

* This method should be called only on ResultSet objects that are scrollable: TYPE_SCROLL_SENSITIVE, TYPE_SCROLL_INSENSITIVE, * TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC. - * - * @return true if the cursor is on the last row in this result set + *

+ * + * Note: Support for the isLast method is optional for ResultSets with a result set type of + * TYPE_FORWARD_ONLY + * + * @return true if the cursor is on the last row; false otherwise + * + * @exception SQLException + * if a database access error occurs or this method is called on a closed result set + * + * @since 1.2 */ - public boolean isLast() throws SQLServerException { + @Override + public boolean isLast() throws SQLException { loggerExternal.entering(getClassNameLogging(), "isLast"); if (logger.isLoggable(java.util.logging.Level.FINER)) logger.finer(toString() + logCursorState()); @@ -1246,7 +1278,8 @@ public boolean isLast() throws SQLServerException { return isLast; } - public void beforeFirst() throws SQLServerException { + @Override + public void beforeFirst() throws SQLException { loggerExternal.entering(getClassNameLogging(), "beforeFirst"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); @@ -1277,7 +1310,8 @@ private void moveBeforeFirst() throws SQLServerException { currentRow = BEFORE_FIRST_ROW; } - public void afterLast() throws SQLServerException { + @Override + public void afterLast() throws SQLException { loggerExternal.entering(getClassNameLogging(), "afterLast"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); @@ -1309,14 +1343,23 @@ private void moveAfterLast() throws SQLServerException { } /** - * Moves the cursor to the first row in this ResultSet object. + * Moves the cursor to the first row in this ResultSet object. * + *

* This method should be called only on ResultSet objects that are scrollable: TYPE_SCROLL_SENSITIVE, TYPE_SCROLL_INSENSITIVE, * TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC. - * - * @return true if the cursor is on a valid row, otherwise returns false if there are no rows in this ResultSet object + *

+ * + * @return true if the cursor is on a valid row; false if there are no rows in the result set + * + * @exception SQLException + * if a database access error occurs; this method is called on a closed result set or the result set type is + * TYPE_FORWARD_ONLY + * + * @since 1.2 */ - public boolean first() throws SQLServerException { + @Override + public boolean first() throws SQLException { loggerExternal.entering(getClassNameLogging(), "first"); if (logger.isLoggable(java.util.logging.Level.FINER)) logger.finer(toString() + logCursorState()); @@ -1359,14 +1402,21 @@ private void moveFirst() throws SQLServerException { } /** - * Moves the cursor to the last row in this ResultSet object. - * + * Moves the cursor to the last row in this ResultSet object. + * * This method should be called only on ResultSet objects that are scrollable: TYPE_SCROLL_SENSITIVE, TYPE_SCROLL_INSENSITIVE, * TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC. * - * @return true if the cursor is on a valid row, otherwise returns false if there are no rows in this ResultSet object + * @return true if the cursor is on a valid row; false if there are no rows in the result set + * + * @exception SQLException + * if a database access error occurs; this method is called on a closed result set or the result set type is + * TYPE_FORWARD_ONLY + * + * @since 1.2 */ - public boolean last() throws SQLServerException { + @Override + public boolean last() throws SQLException { loggerExternal.entering(getClassNameLogging(), "last"); if (logger.isLoggable(java.util.logging.Level.FINER)) logger.finer(toString() + logCursorState()); @@ -1413,12 +1463,8 @@ private void moveLast() throws SQLServerException { currentRow = isDynamic() ? UNKNOWN_ROW : rowCount; } - /** - * Retrieves the number of the current row in this ResultSet object. The first row is number 1, the second is 2, and so on. - * - * @return the number of the current row; 0 if there is no current row - */ - public int getRow() throws SQLServerException { + @Override + public int getRow() throws SQLException { loggerExternal.entering(getClassNameLogging(), "getRow"); if (logger.isLoggable(java.util.logging.Level.FINER)) logger.finer(toString() + logCursorState()); @@ -1447,15 +1493,48 @@ public int getRow() throws SQLServerException { } /** - * Moves the cursor to the specified row in this ResultSet object. The specified row may be positive, negative or zero. + * Moves the cursor to the given row number in this ResultSet object. * + *

* This method should be called only on ResultSet objects that are scrollable: TYPE_SCROLL_SENSITIVE, TYPE_SCROLL_INSENSITIVE, * TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC. + *

+ * + *

+ * If the row number is positive, the cursor moves to the given row number with respect to the beginning of the result set. The first row is row + * 1, the second is row 2, and so on. * - * @return true if the cursor is on a valid row in this result set, otherwise returns false if the cursor is before the first row or after the - * last row + *

+ * If the given row number is negative, the cursor moves to an absolute row position with respect to the end of the result set. For example, + * calling the method absolute(-1) positions the cursor on the last row; calling the method absolute(-2) moves the + * cursor to the next-to-last row, and so on. + * + *

+ * If the row number specified is zero, the cursor is moved to before the first row. + * + *

+ * An attempt to position the cursor beyond the first/last row in the result set leaves the cursor before the first row or after the last row. + * + *

+ * Note: Calling absolute(1) is the same as calling first(). Calling absolute(-1) is the same as + * calling last(). + * + * @param row + * the number of the row to which the cursor should move. A value of zero indicates that the cursor will be positioned before the first + * row; a positive number indicates the row number counting from the beginning of the result set; a negative number indicates the row + * number counting from the end of the result set + * + * @return true if the cursor is moved to a position in this ResultSet object; false if the cursor is + * before the first row or after the last row + * + * @exception SQLException + * if a database access error occurs; this method is called on a closed result set or the result set type is + * TYPE_FORWARD_ONLY + * + * @since 1.2 */ - public boolean absolute(int row) throws SQLServerException { + @Override + public boolean absolute(int row) throws SQLException { loggerExternal.entering(getClassNameLogging(), "absolute"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); @@ -1763,14 +1842,21 @@ private int clientMoveAbsolute(int row) throws SQLServerException { } /** - * Moves the cursor to the previous row in this ResultSet object. + * Moves the cursor to the previous row in this ResultSet object. * * This method should be called only on ResultSet objects that are scrollable: TYPE_SCROLL_SENSITIVE, TYPE_SCROLL_INSENSITIVE, * TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC. * - * @return true if the cursor is on a valid row in this result set + * @return true if the cursor is now positioned on a valid row; false if the cursor is positioned before the first row + * + * @exception SQLException + * if a database access error occurs; this method is called on a closed result set or the result set type is + * TYPE_FORWARD_ONLY + * + * @since 1.2 */ - public boolean previous() throws SQLServerException { + @Override + public boolean previous() throws SQLException { loggerExternal.entering(getClassNameLogging(), "previous"); if (logger.isLoggable(java.util.logging.Level.FINER)) logger.finer(toString() + logCursorState()); @@ -1805,17 +1891,19 @@ private void cancelInsert() { /** Clear any updated column values for the current row in the result set. */ final void clearColumnsValues() { - int l = columns.length; - for (Column column : columns) column.cancelUpdates(); + for (Column column : columns) + column.cancelUpdates(); } - /* L0 */ public SQLWarning getWarnings() throws SQLServerException { + @Override + public SQLWarning getWarnings() throws SQLException { loggerExternal.entering(getClassNameLogging(), "getWarnings"); loggerExternal.exiting(getClassNameLogging(), "getWarnings", null); return null; } - public void setFetchDirection(int direction) throws SQLServerException { + @Override + public void setFetchDirection(int direction) throws SQLException { loggerExternal.entering(getClassNameLogging(), "setFetchDirection", direction); checkClosed(); @@ -1836,13 +1924,15 @@ public void setFetchDirection(int direction) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "setFetchDirection"); } - public int getFetchDirection() throws SQLServerException { + @Override + public int getFetchDirection() throws SQLException { loggerExternal.entering(getClassNameLogging(), "getFetchDirection"); checkClosed(); loggerExternal.exiting(getClassNameLogging(), "getFetchDirection", fetchDirection); return fetchDirection; } + @Override public void setFetchSize(int rows) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "setFetchSize", rows); checkClosed(); @@ -1853,14 +1943,16 @@ public void setFetchSize(int rows) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "setFetchSize"); } - /* L0 */ public int getFetchSize() throws SQLServerException { + @Override + public int getFetchSize() throws SQLException { loggerExternal.entering(getClassNameLogging(), "getFetchSize"); checkClosed(); loggerExternal.exiting(getClassNameLogging(), "getFloat", fetchSize); return fetchSize; } - /* L0 */ public int getType() throws SQLServerException { + @Override + public int getType() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getType"); checkClosed(); @@ -1869,7 +1961,8 @@ public void setFetchSize(int rows) throws SQLServerException { return value; } - /* L0 */ public int getConcurrency() throws SQLServerException { + @Override + public int getConcurrency() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getConcurrency"); checkClosed(); int value = stmt.getResultSetConcurrency(); @@ -1937,15 +2030,16 @@ private Object getValue(int columnIndex, lastValueWasNull = (null == o); return o; } - - void setInternalVariantType(int columnIndex, SqlVariant type) throws SQLServerException{ + + void setInternalVariantType(int columnIndex, + SqlVariant type) throws SQLServerException { getterGetColumn(columnIndex).setInternalVariant(type); } - + SqlVariant getVariantInternalType(int columnIndex) throws SQLServerException { return getterGetColumn(columnIndex).getInternalVariant(); - } - + } + private Object getStream(int columnIndex, StreamType streamType) throws SQLServerException { Object value = getValue(columnIndex, streamType.getJDBCType(), @@ -1964,6 +2058,7 @@ private SQLXML getSQLXMLInternal(int columnIndex) throws SQLServerException { return value; } + @Override public java.io.InputStream getAsciiStream(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getAsciiStream", columnIndex); checkClosed(); @@ -1972,6 +2067,7 @@ public java.io.InputStream getAsciiStream(int columnIndex) throws SQLServerExcep return value; } + @Override public java.io.InputStream getAsciiStream(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getAsciiStream", columnName); checkClosed(); @@ -1981,6 +2077,7 @@ public java.io.InputStream getAsciiStream(String columnName) throws SQLServerExc } @Deprecated + @Override public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -1994,6 +2091,7 @@ public BigDecimal getBigDecimal(int columnIndex, } @Deprecated + @Override public BigDecimal getBigDecimal(String columnName, int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2006,6 +2104,7 @@ public BigDecimal getBigDecimal(String columnName, return value; } + @Override public java.io.InputStream getBinaryStream(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getBinaryStream", columnIndex); checkClosed(); @@ -2014,6 +2113,7 @@ public java.io.InputStream getBinaryStream(int columnIndex) throws SQLServerExce return value; } + @Override public java.io.InputStream getBinaryStream(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getBinaryStream", columnName); checkClosed(); @@ -2022,6 +2122,7 @@ public java.io.InputStream getBinaryStream(String columnName) throws SQLServerEx return value; } + @Override public boolean getBoolean(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getBoolean", columnIndex); checkClosed(); @@ -2030,6 +2131,7 @@ public boolean getBoolean(int columnIndex) throws SQLServerException { return null != value ? value : false; } + @Override public boolean getBoolean(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getBoolean", columnName); checkClosed(); @@ -2038,6 +2140,7 @@ public boolean getBoolean(String columnName) throws SQLServerException { return null != value ? value : false; } + @Override public byte getByte(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getByte", columnIndex); checkClosed(); @@ -2046,6 +2149,7 @@ public byte getByte(int columnIndex) throws SQLServerException { return null != value ? value.byteValue() : 0; } + @Override public byte getByte(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getByte", columnName); checkClosed(); @@ -2054,6 +2158,7 @@ public byte getByte(String columnName) throws SQLServerException { return null != value ? value.byteValue() : 0; } + @Override public byte[] getBytes(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getBytes", columnIndex); checkClosed(); @@ -2062,6 +2167,7 @@ public byte[] getBytes(int columnIndex) throws SQLServerException { return value; } + @Override public byte[] getBytes(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getBytes", columnName); checkClosed(); @@ -2070,6 +2176,7 @@ public byte[] getBytes(String columnName) throws SQLServerException { return value; } + @Override public java.sql.Date getDate(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getDate", columnIndex); checkClosed(); @@ -2078,6 +2185,7 @@ public java.sql.Date getDate(int columnIndex) throws SQLServerException { return value; } + @Override public java.sql.Date getDate(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getDate", columnName); checkClosed(); @@ -2086,6 +2194,7 @@ public java.sql.Date getDate(String columnName) throws SQLServerException { return value; } + @Override public java.sql.Date getDate(int columnIndex, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2096,6 +2205,7 @@ public java.sql.Date getDate(int columnIndex, return value; } + @Override public java.sql.Date getDate(String colName, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2106,6 +2216,7 @@ public java.sql.Date getDate(String colName, return value; } + @Override public double getDouble(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getDouble", columnIndex); checkClosed(); @@ -2114,6 +2225,7 @@ public double getDouble(int columnIndex) throws SQLServerException { return null != value ? value : 0; } + @Override public double getDouble(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getDouble", columnName); checkClosed(); @@ -2122,6 +2234,7 @@ public double getDouble(String columnName) throws SQLServerException { return null != value ? value : 0; } + @Override public float getFloat(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getFloat", columnIndex); checkClosed(); @@ -2130,6 +2243,7 @@ public float getFloat(int columnIndex) throws SQLServerException { return null != value ? value : 0; } + @Override public float getFloat(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getFloat", columnName); checkClosed(); @@ -2137,7 +2251,8 @@ public float getFloat(String columnName) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "getFloat", value); return null != value ? value : 0; } - + + @Override public Geometry getGeometry(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getFloat", columnIndex); checkClosed(); @@ -2146,6 +2261,7 @@ public Geometry getGeometry(int columnIndex) throws SQLServerException { return value; } + @Override public Geometry getGeometry(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getFloat", columnName); checkClosed(); @@ -2153,7 +2269,8 @@ public Geometry getGeometry(String columnName) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "getFloat", value); return value; } - + + @Override public Geography getGeography(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getFloat", columnIndex); checkClosed(); @@ -2162,6 +2279,7 @@ public Geography getGeography(int columnIndex) throws SQLServerException { return value; } + @Override public Geography getGeography(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getFloat", columnName); checkClosed(); @@ -2170,6 +2288,7 @@ public Geography getGeography(String columnName) throws SQLServerException { return value; } + @Override public int getInt(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getInt", columnIndex); checkClosed(); @@ -2178,6 +2297,7 @@ public int getInt(int columnIndex) throws SQLServerException { return null != value ? value : 0; } + @Override public int getInt(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getInt", columnName); checkClosed(); @@ -2186,6 +2306,7 @@ public int getInt(String columnName) throws SQLServerException { return null != value ? value : 0; } + @Override public long getLong(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getLong", columnIndex); checkClosed(); @@ -2194,6 +2315,7 @@ public long getLong(int columnIndex) throws SQLServerException { return null != value ? value : 0; } + @Override public long getLong(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getLong", columnName); checkClosed(); @@ -2202,6 +2324,7 @@ public long getLong(String columnName) throws SQLServerException { return null != value ? value : 0; } + @Override public java.sql.ResultSetMetaData getMetaData() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getMetaData"); checkClosed(); @@ -2211,6 +2334,7 @@ public java.sql.ResultSetMetaData getMetaData() throws SQLServerException { return metaData; } + @Override public Object getObject(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getObject", columnIndex); checkClosed(); @@ -2219,6 +2343,7 @@ public Object getObject(int columnIndex) throws SQLServerException { return value; } + @Override public T getObject(int columnIndex, Class type) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getObject", columnIndex); @@ -2301,6 +2426,7 @@ else if (type == Double.class) { return type.cast(returnValue); } + @Override public Object getObject(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getObject", columnName); checkClosed(); @@ -2309,6 +2435,7 @@ public Object getObject(String columnName) throws SQLServerException { return value; } + @Override public T getObject(String columnName, Class type) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getObject", columnName); @@ -2318,6 +2445,7 @@ public T getObject(String columnName, return value; } + @Override public short getShort(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getShort", columnIndex); checkClosed(); @@ -2326,6 +2454,7 @@ public short getShort(int columnIndex) throws SQLServerException { return null != value ? value : 0; } + @Override public short getShort(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getShort", columnName); checkClosed(); @@ -2334,6 +2463,7 @@ public short getShort(String columnName) throws SQLServerException { return null != value ? value : 0; } + @Override public String getString(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getString", columnIndex); checkClosed(); @@ -2347,6 +2477,7 @@ public String getString(int columnIndex) throws SQLServerException { return value; } + @Override public String getString(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getString", columnName); checkClosed(); @@ -2360,6 +2491,7 @@ public String getString(String columnName) throws SQLServerException { return value; } + @Override public String getNString(int columnIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNString", columnIndex); checkClosed(); @@ -2368,6 +2500,7 @@ public String getNString(int columnIndex) throws SQLException { return value; } + @Override public String getNString(String columnLabel) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNString", columnLabel); checkClosed(); @@ -2376,17 +2509,8 @@ public String getNString(String columnLabel) throws SQLException { return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a microsoft.sql.datetimeoffset object in the Java - * programming language. - * - * @param columnIndex - * the first column is 1, the second is 2, ... - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLException - * when an error occurs - */ - public String getUniqueIdentifier(int columnIndex) throws SQLException { + @Override + public String getUniqueIdentifier(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getUniqueIdentifier", columnIndex); checkClosed(); String value = (String) getValue(columnIndex, JDBCType.GUID); @@ -2394,17 +2518,8 @@ public String getUniqueIdentifier(int columnIndex) throws SQLException { return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a microsoft.sql.datetimeoffset object in the Java - * programming language. - * - * @param columnLabel - * the name of the column - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLException - * when an error occurs - */ - public String getUniqueIdentifier(String columnLabel) throws SQLException { + @Override + public String getUniqueIdentifier(String columnLabel) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getUniqueIdentifier", columnLabel); checkClosed(); String value = (String) getValue(findColumn(columnLabel), JDBCType.GUID); @@ -2412,6 +2527,7 @@ public String getUniqueIdentifier(String columnLabel) throws SQLException { return value; } + @Override public java.sql.Time getTime(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getTime", columnIndex); checkClosed(); @@ -2420,6 +2536,7 @@ public java.sql.Time getTime(int columnIndex) throws SQLServerException { return value; } + @Override public java.sql.Time getTime(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getTime", columnName); checkClosed(); @@ -2428,6 +2545,7 @@ public java.sql.Time getTime(String columnName) throws SQLServerException { return value; } + @Override public java.sql.Time getTime(int columnIndex, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2438,6 +2556,7 @@ public java.sql.Time getTime(int columnIndex, return value; } + @Override public java.sql.Time getTime(String colName, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2448,6 +2567,7 @@ public java.sql.Time getTime(String colName, return value; } + @Override public java.sql.Timestamp getTimestamp(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getTimestamp", columnIndex); checkClosed(); @@ -2456,6 +2576,7 @@ public java.sql.Timestamp getTimestamp(int columnIndex) throws SQLServerExceptio return value; } + @Override public java.sql.Timestamp getTimestamp(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getTimestamp", columnName); checkClosed(); @@ -2464,6 +2585,7 @@ public java.sql.Timestamp getTimestamp(String columnName) throws SQLServerExcept return value; } + @Override public java.sql.Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2474,6 +2596,7 @@ public java.sql.Timestamp getTimestamp(int columnIndex, return value; } + @Override public java.sql.Timestamp getTimestamp(String colName, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2484,16 +2607,7 @@ public java.sql.Timestamp getTimestamp(String colName, return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming - * language. - * - * @param columnIndex - * the first column is 1, the second is 2, ... - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * when an error occurs - */ + @Override public java.sql.Timestamp getDateTime(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getDateTime", columnIndex); checkClosed(); @@ -2502,16 +2616,7 @@ public java.sql.Timestamp getDateTime(int columnIndex) throws SQLServerException return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming - * language. - * - * @param columnName - * is the name of the column - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * If any errors occur. - */ + @Override public java.sql.Timestamp getDateTime(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getDateTime", columnName); checkClosed(); @@ -2520,19 +2625,7 @@ public java.sql.Timestamp getDateTime(String columnName) throws SQLServerExcepti return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming - * language. This method uses the given calendar to construct an appropriate millisecond value for the timestamp if the underlying database does - * not store timezone information. - * - * @param columnIndex - * the first column is 1, the second is 2, ... - * @param cal - * the java.util.Calendar object to use in constructing the dateTime - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * If any errors occur. - */ + @Override public java.sql.Timestamp getDateTime(int columnIndex, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2543,20 +2636,7 @@ public java.sql.Timestamp getDateTime(int columnIndex, return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming - * language. This method uses the given calendar to construct an appropriate millisecond value for the timestamp if the underlying database does - * not store timezone information. - * - * @param colName - * the label for the column specified with the SQL AS clause. If the SQL AS clause was not specified, then the label is the name of the - * column - * @param cal - * the java.util.Calendar object to use in constructing the dateTime - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * If any errors occur. - */ + @Override public java.sql.Timestamp getDateTime(String colName, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2567,16 +2647,7 @@ public java.sql.Timestamp getDateTime(String colName, return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming - * language. - * - * @param columnIndex - * the first column is 1, the second is 2, ... - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * when an error occurs - */ + @Override public java.sql.Timestamp getSmallDateTime(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getSmallDateTime", columnIndex); checkClosed(); @@ -2585,16 +2656,7 @@ public java.sql.Timestamp getSmallDateTime(int columnIndex) throws SQLServerExce return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming - * language. - * - * @param columnName - * is the name of a column. - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * If any errors occur. - */ + @Override public java.sql.Timestamp getSmallDateTime(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getSmallDateTime", columnName); checkClosed(); @@ -2603,18 +2665,7 @@ public java.sql.Timestamp getSmallDateTime(String columnName) throws SQLServerEx return value; } - /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming - * language. - * - * @param columnIndex - * the first column is 1, the second is 2, ... - * @param cal - * the java.util.Calendar object to use in constructing the smalldateTime - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * If any errors occur. - */ + @Override public java.sql.Timestamp getSmallDateTime(int columnIndex, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2625,16 +2676,7 @@ public java.sql.Timestamp getSmallDateTime(int columnIndex, return value; } - /** - * - * @param colName - * The name of a column - * @param cal - * the java.util.Calendar object to use in constructing the smalldateTime - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * If any errors occur. - */ + @Override public java.sql.Timestamp getSmallDateTime(String colName, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2645,7 +2687,8 @@ public java.sql.Timestamp getSmallDateTime(String colName, return value; } - public microsoft.sql.DateTimeOffset getDateTimeOffset(int columnIndex) throws SQLException { + @Override + public microsoft.sql.DateTimeOffset getDateTimeOffset(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getDateTimeOffset", columnIndex); checkClosed(); @@ -2659,7 +2702,8 @@ public microsoft.sql.DateTimeOffset getDateTimeOffset(int columnIndex) throws SQ return value; } - public microsoft.sql.DateTimeOffset getDateTimeOffset(String columnName) throws SQLException { + @Override + public microsoft.sql.DateTimeOffset getDateTimeOffset(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getDateTimeOffset", columnName); checkClosed(); @@ -2673,34 +2717,39 @@ public microsoft.sql.DateTimeOffset getDateTimeOffset(String columnName) throws return value; } + @Override @Deprecated - public java.io.InputStream getUnicodeStream(int columnIndex) throws SQLServerException { + public java.io.InputStream getUnicodeStream(int columnIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getUnicodeStream", columnIndex); - NotImplemented(); + SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } + @Override @Deprecated - public java.io.InputStream getUnicodeStream(String columnName) throws SQLServerException { + public java.io.InputStream getUnicodeStream(String columnName) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getUnicodeStream", columnName); - NotImplemented(); + SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } + @Override public Object getObject(int i, - java.util.Map> map) throws SQLServerException { + java.util.Map> map) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getObject", new Object[] {i, map}); - NotImplemented(); + SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } - public Ref getRef(int i) throws SQLServerException { + @Override + public Ref getRef(int i) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getRef"); - NotImplemented(); + SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } + @Override public Blob getBlob(int i) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getBlob", i); checkClosed(); @@ -2710,6 +2759,7 @@ public Blob getBlob(int i) throws SQLServerException { return value; } + @Override public Blob getBlob(String colName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getBlob", colName); checkClosed(); @@ -2719,6 +2769,7 @@ public Blob getBlob(String colName) throws SQLServerException { return value; } + @Override public Clob getClob(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getClob", columnIndex); checkClosed(); @@ -2728,6 +2779,7 @@ public Clob getClob(int columnIndex) throws SQLServerException { return value; } + @Override public Clob getClob(String colName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getClob", colName); checkClosed(); @@ -2737,6 +2789,7 @@ public Clob getClob(String colName) throws SQLServerException { return value; } + @Override public NClob getNClob(int columnIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNClob", columnIndex); checkClosed(); @@ -2746,6 +2799,7 @@ public NClob getNClob(int columnIndex) throws SQLException { return value; } + @Override public NClob getNClob(String columnLabel) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNClob", columnLabel); checkClosed(); @@ -2755,35 +2809,41 @@ public NClob getNClob(String columnLabel) throws SQLException { return value; } - public Array getArray(int i) throws SQLServerException { - NotImplemented(); + @Override + public Array getArray(int i) throws SQLException { + SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } + @Override public Object getObject(String colName, - java.util.Map> map) throws SQLServerException { - NotImplemented(); + java.util.Map> map) throws SQLException { + SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } - public Ref getRef(String colName) throws SQLServerException { - NotImplemented(); + @Override + public Ref getRef(String colName) throws SQLException { + SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } - public Array getArray(String colName) throws SQLServerException { - NotImplemented(); + @Override + public Array getArray(String colName) throws SQLException { + SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } - public String getCursorName() throws SQLServerException { + @Override + public String getCursorName() throws SQLException { loggerExternal.entering(getClassNameLogging(), "getCursorName"); SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_positionedUpdatesNotSupported"), null, false); loggerExternal.exiting(getClassNameLogging(), "getCursorName", null); return null; } - public java.io.Reader getCharacterStream(int columnIndex) throws SQLServerException { + @Override + public java.io.Reader getCharacterStream(int columnIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getCharacterStream", columnIndex); checkClosed(); Reader value = (Reader) getStream(columnIndex, StreamType.CHARACTER); @@ -2791,7 +2851,8 @@ public java.io.Reader getCharacterStream(int columnIndex) throws SQLServerExcept return value; } - public java.io.Reader getCharacterStream(String columnName) throws SQLServerException { + @Override + public java.io.Reader getCharacterStream(String columnName) throws SQLException { checkClosed(); loggerExternal.entering(getClassNameLogging(), "getCharacterStream", columnName); Reader value = (Reader) getStream(findColumn(columnName), StreamType.CHARACTER); @@ -2799,6 +2860,7 @@ public java.io.Reader getCharacterStream(String columnName) throws SQLServerExce return value; } + @Override public Reader getNCharacterStream(int columnIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNCharacterStream", columnIndex); checkClosed(); @@ -2807,6 +2869,7 @@ public Reader getNCharacterStream(int columnIndex) throws SQLException { return value; } + @Override public Reader getNCharacterStream(String columnLabel) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNCharacterStream", columnLabel); checkClosed(); @@ -2815,7 +2878,8 @@ public Reader getNCharacterStream(String columnLabel) throws SQLException { return value; } - public BigDecimal getBigDecimal(int columnIndex) throws SQLServerException { + @Override + public BigDecimal getBigDecimal(int columnIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getBigDecimal", columnIndex); checkClosed(); BigDecimal value = (BigDecimal) getValue(columnIndex, JDBCType.DECIMAL); @@ -2823,7 +2887,8 @@ public BigDecimal getBigDecimal(int columnIndex) throws SQLServerException { return value; } - public BigDecimal getBigDecimal(String columnName) throws SQLServerException { + @Override + public BigDecimal getBigDecimal(String columnName) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getBigDecimal", columnName); checkClosed(); BigDecimal value = (BigDecimal) getValue(findColumn(columnName), JDBCType.DECIMAL); @@ -2831,15 +2896,7 @@ public BigDecimal getBigDecimal(String columnName) throws SQLServerException { return value; } - /** - * Retrieves the value of the column specified as a java.math.BigDecimal object. - * - * @param columnIndex - * The zero-based ordinal of a column. - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * when an error occurs - */ + @Override public BigDecimal getMoney(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getMoney", columnIndex); checkClosed(); @@ -2848,15 +2905,7 @@ public BigDecimal getMoney(int columnIndex) throws SQLServerException { return value; } - /** - * Retrieves the value of the column specified as a java.math.BigDecimal object. - * - * @param columnName - * is the name of a column. - * @return the column value; if the value is SQL NULL, the value returned is null. - * @throws SQLServerException - * If any errors occur. - */ + @Override public BigDecimal getMoney(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getMoney", columnName); checkClosed(); @@ -2865,15 +2914,7 @@ public BigDecimal getMoney(String columnName) throws SQLServerException { return value; } - /** - * Retrieves the value of the column specified as a java.math.BigDecimal object. - * - * @param columnIndex - * The zero-based ordinal of a column. - * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLServerException - * If any errors occur. - */ + @Override public BigDecimal getSmallMoney(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getSmallMoney", columnIndex); checkClosed(); @@ -2882,15 +2923,7 @@ public BigDecimal getSmallMoney(int columnIndex) throws SQLServerException { return value; } - /** - * Retrieves the value of the column specified as a java.math.BigDecimal object. - * - * @param columnName - * is the name of a column. - * @return the column value; if the value is SQL NULL, the value returned is null. - * @throws SQLServerException - * If any errors occur. - */ + @Override public BigDecimal getSmallMoney(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getSmallMoney", columnName); checkClosed(); @@ -2899,17 +2932,19 @@ public BigDecimal getSmallMoney(String columnName) throws SQLServerException { return value; } + @Override public RowId getRowId(int columnIndex) throws SQLException { - // Not implemented - throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + SQLServerException.throwNotSupportedException(stmt.connection, stmt); + return null; } + @Override public RowId getRowId(String columnLabel) throws SQLException { - - // Not implemented - throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + SQLServerException.throwNotSupportedException(stmt.connection, stmt); + return null; } + @Override public SQLXML getSQLXML(int columnIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getSQLXML", columnIndex); SQLXML xml = getSQLXMLInternal(columnIndex); @@ -2917,6 +2952,7 @@ public SQLXML getSQLXML(int columnIndex) throws SQLException { return xml; } + @Override public SQLXML getSQLXML(String columnLabel) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getSQLXML", columnLabel); SQLXML xml = getSQLXMLInternal(findColumn(columnLabel)); @@ -2924,6 +2960,7 @@ public SQLXML getSQLXML(String columnLabel) throws SQLException { return xml; } + @Override public boolean rowUpdated() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "rowUpdated"); checkClosed(); @@ -2937,6 +2974,7 @@ public boolean rowUpdated() throws SQLServerException { return false; } + @Override public boolean rowInserted() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "rowInserted"); checkClosed(); @@ -2951,6 +2989,7 @@ public boolean rowInserted() throws SQLServerException { return false; } + @Override public boolean rowDeleted() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "rowDeleted"); checkClosed(); @@ -3068,6 +3107,7 @@ private void updateSQLXMLInternal(int columnIndex, stmt.stmtColumnEncriptionSetting, null, false, columnIndex); } + @Override public void updateNull(int index) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "updateNull", index); @@ -3077,8 +3117,9 @@ public void updateNull(int index) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "updateNull"); } + @Override public void updateBoolean(int index, - boolean x) throws SQLServerException { + boolean x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateBoolean", new Object[] {index, x}); checkClosed(); @@ -3087,22 +3128,7 @@ public void updateBoolean(int index, loggerExternal.exiting(getClassNameLogging(), "updateBoolean"); } - /** - * Updates the designated column with a boolean value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateBoolean(int index, boolean x, boolean forceEncrypt) throws SQLServerException { @@ -3114,8 +3140,9 @@ public void updateBoolean(int index, loggerExternal.exiting(getClassNameLogging(), "updateBoolean"); } + @Override public void updateByte(int index, - byte x) throws SQLServerException { + byte x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateByte", new Object[] {index, x}); @@ -3125,22 +3152,7 @@ public void updateByte(int index, loggerExternal.exiting(getClassNameLogging(), "updateByte"); } - /** - * Updates the designated column with a byte value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateByte(int index, byte x, boolean forceEncrypt) throws SQLServerException { @@ -3153,8 +3165,9 @@ public void updateByte(int index, loggerExternal.exiting(getClassNameLogging(), "updateByte"); } + @Override public void updateShort(int index, - short x) throws SQLServerException { + short x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateShort", new Object[] {index, x}); @@ -3164,22 +3177,7 @@ public void updateShort(int index, loggerExternal.exiting(getClassNameLogging(), "updateShort"); } - /** - * Updates the designated column with a short value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateShort(int index, short x, boolean forceEncrypt) throws SQLServerException { @@ -3192,8 +3190,9 @@ public void updateShort(int index, loggerExternal.exiting(getClassNameLogging(), "updateShort"); } + @Override public void updateInt(int index, - int x) throws SQLServerException { + int x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateInt", new Object[] {index, x}); @@ -3203,22 +3202,7 @@ public void updateInt(int index, loggerExternal.exiting(getClassNameLogging(), "updateInt"); } - /** - * Updates the designated column with an int value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateInt(int index, int x, boolean forceEncrypt) throws SQLServerException { @@ -3231,8 +3215,9 @@ public void updateInt(int index, loggerExternal.exiting(getClassNameLogging(), "updateInt"); } + @Override public void updateLong(int index, - long x) throws SQLServerException { + long x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateLong", new Object[] {index, x}); @@ -3242,22 +3227,7 @@ public void updateLong(int index, loggerExternal.exiting(getClassNameLogging(), "updateLong"); } - /** - * Updates the designated column with a long value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateLong(int index, long x, boolean forceEncrypt) throws SQLServerException { @@ -3270,8 +3240,9 @@ public void updateLong(int index, loggerExternal.exiting(getClassNameLogging(), "updateLong"); } + @Override public void updateFloat(int index, - float x) throws SQLServerException { + float x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateFloat", new Object[] {index, x}); @@ -3281,22 +3252,7 @@ public void updateFloat(int index, loggerExternal.exiting(getClassNameLogging(), "updateFloat"); } - /** - * Updates the designated column with a float value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateFloat(int index, float x, boolean forceEncrypt) throws SQLServerException { @@ -3309,8 +3265,9 @@ public void updateFloat(int index, loggerExternal.exiting(getClassNameLogging(), "updateFloat"); } + @Override public void updateDouble(int index, - double x) throws SQLServerException { + double x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateDouble", new Object[] {index, x}); @@ -3320,22 +3277,7 @@ public void updateDouble(int index, loggerExternal.exiting(getClassNameLogging(), "updateDouble"); } - /** - * Updates the designated column with a double value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateDouble(int index, double x, boolean forceEncrypt) throws SQLServerException { @@ -3348,18 +3290,7 @@ public void updateDouble(int index, loggerExternal.exiting(getClassNameLogging(), "updateDouble"); } - /** - * Updates the designated column with a money value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateMoney(int index, BigDecimal x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3370,22 +3301,7 @@ public void updateMoney(int index, loggerExternal.exiting(getClassNameLogging(), "updateMoney"); } - /** - * Updates the designated column with a money value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateMoney(int index, BigDecimal x, boolean forceEncrypt) throws SQLServerException { @@ -3397,18 +3313,7 @@ public void updateMoney(int index, loggerExternal.exiting(getClassNameLogging(), "updateMoney"); } - /** - * Updates the designated column with a money value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param columnName - * is the column name - * @param x - * the new column value - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateMoney(String columnName, BigDecimal x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3419,22 +3324,7 @@ public void updateMoney(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateMoney"); } - /** - * Updates the designated column with a money value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param columnName - * the column name - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateMoney(String columnName, BigDecimal x, boolean forceEncrypt) throws SQLServerException { @@ -3446,18 +3336,7 @@ public void updateMoney(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateMoney"); } - /** - * Updates the designated column with a smallmoney value. The updater methods are used to update column values in the current row or - * the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods - * are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateSmallMoney(int index, BigDecimal x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3468,22 +3347,7 @@ public void updateSmallMoney(int index, loggerExternal.exiting(getClassNameLogging(), "updateSmallMoney"); } - /** - * Updates the designated column with a smallmoney value. The updater methods are used to update column values in the current row or - * the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods - * are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateSmallMoney(int index, BigDecimal x, boolean forceEncrypt) throws SQLServerException { @@ -3495,18 +3359,7 @@ public void updateSmallMoney(int index, loggerExternal.exiting(getClassNameLogging(), "updateSmallMoney"); } - /** - * Updates the designated column with a smallmoney value. The updater methods are used to update column values in the current row or - * the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods - * are called to update the database. - * - * @param columnName - * the column name - * @param x - * the new column value - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateSmallMoney(String columnName, BigDecimal x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3517,22 +3370,7 @@ public void updateSmallMoney(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateSmallMoney"); } - /** - * Updates the designated column with a smallmoney value. The updater methods are used to update column values in the current row or - * the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods - * are called to update the database. - * - * @param columnName - * the column name - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateSmallMoney(String columnName, BigDecimal x, boolean forceEncrypt) throws SQLServerException { @@ -3544,6 +3382,7 @@ public void updateSmallMoney(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateSmallMoney"); } + @Override public void updateBigDecimal(int index, BigDecimal x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3555,22 +3394,7 @@ public void updateBigDecimal(int index, loggerExternal.exiting(getClassNameLogging(), "updateBigDecimal"); } - /** - * Updates the designated column with a java.math.BigDecimal value. The updater methods are used to update column values in the - * current row or the insert row. The updater methods do not update the underlying database; instead the updateRow or - * insertRow methods are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param precision - * the precision of the column - * @param scale - * the scale of the column - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateBigDecimal(int index, BigDecimal x, Integer precision, @@ -3584,26 +3408,7 @@ public void updateBigDecimal(int index, loggerExternal.exiting(getClassNameLogging(), "updateBigDecimal"); } - /** - * Updates the designated column with a java.math.BigDecimal value. The updater methods are used to update column values in the - * current row or the insert row. The updater methods do not update the underlying database; instead the updateRow or - * insertRow methods are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param precision - * the precision of the column - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateBigDecimal(int index, BigDecimal x, Integer precision, @@ -3618,6 +3423,7 @@ public void updateBigDecimal(int index, loggerExternal.exiting(getClassNameLogging(), "updateBigDecimal"); } + @Override public void updateString(int columnIndex, String stringValue) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3629,22 +3435,7 @@ public void updateString(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateString"); } - /** - * Updates the designated column with a String value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param columnIndex - * the first column is 1, the second is 2, ... - * @param stringValue - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateString(int columnIndex, String stringValue, boolean forceEncrypt) throws SQLServerException { @@ -3657,6 +3448,7 @@ public void updateString(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateString"); } + @Override public void updateNString(int columnIndex, String nString) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3668,26 +3460,10 @@ public void updateNString(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateNString"); } - /** - * Updates the designated column with a String value. It is intended for use when updating NCHAR,NVARCHAR - * and LONGNVARCHAR columns. The updater methods are used to update column values in the current row or the insert row. The updater - * methods do not update the underlying database; instead the updateRow or insertRow methods are called to update the - * database. - * - * @param columnIndex - * the first column is 1, the second 2, ... - * @param nString - * the value for the column to be updated - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLException - * when an error occurs - */ + @Override public void updateNString(int columnIndex, String nString, - boolean forceEncrypt) throws SQLException { + boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateNString", new Object[] {columnIndex, nString, forceEncrypt}); @@ -3697,6 +3473,7 @@ public void updateNString(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateNString"); } + @Override public void updateNString(String columnLabel, String nString) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3708,27 +3485,10 @@ public void updateNString(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateNString"); } - /** - * Updates the designated column with a String value. It is intended for use when updating NCHAR,NVARCHAR - * and LONGNVARCHAR columns. The updater methods are used to update column values in the current row or the insert row. The updater - * methods do not update the underlying database; instead the updateRow or insertRow methods are called to update the - * database. - * - * @param columnLabel - * the label for the column specified with the SQL AS clause. If the SQL AS clause was not specified, then the label is the name of the - * column - * @param nString - * the value for the column to be updated - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLException - * when an error occurs - */ + @Override public void updateNString(String columnLabel, String nString, - boolean forceEncrypt) throws SQLException { + boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateNString", new Object[] {columnLabel, nString, forceEncrypt}); @@ -3738,8 +3498,9 @@ public void updateNString(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateNString"); } + @Override public void updateBytes(int index, - byte x[]) throws SQLServerException { + byte x[]) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateBytes", new Object[] {index, x}); @@ -3749,22 +3510,7 @@ public void updateBytes(int index, loggerExternal.exiting(getClassNameLogging(), "updateBytes"); } - /** - * Updates the designated column with a byte array value. The updater methods are used to update column values in the current row or - * the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods - * are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateBytes(int index, byte x[], boolean forceEncrypt) throws SQLServerException { @@ -3777,6 +3523,7 @@ public void updateBytes(int index, loggerExternal.exiting(getClassNameLogging(), "updateBytes"); } + @Override public void updateDate(int index, java.sql.Date x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3788,22 +3535,7 @@ public void updateDate(int index, loggerExternal.exiting(getClassNameLogging(), "updateDate"); } - /** - * Updates the designated column with a java.sql.Date value. The updater methods are used to update column values in the current row - * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateDate(int index, java.sql.Date x, boolean forceEncrypt) throws SQLServerException { @@ -3816,6 +3548,7 @@ public void updateDate(int index, loggerExternal.exiting(getClassNameLogging(), "updateDate"); } + @Override public void updateTime(int index, java.sql.Time x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3827,20 +3560,7 @@ public void updateTime(int index, loggerExternal.exiting(getClassNameLogging(), "updateTime"); } - /** - * Updates the designated column with a java.sql.Time value. The updater methods are used to update column values in the current row - * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param scale - * the scale of the column - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateTime(int index, java.sql.Time x, Integer scale) throws SQLServerException { @@ -3853,24 +3573,7 @@ public void updateTime(int index, loggerExternal.exiting(getClassNameLogging(), "updateTime"); } - /** - * Updates the designated column with a java.sql.Time value. The updater methods are used to update column values in the current row - * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateTime(int index, java.sql.Time x, Integer scale, @@ -3884,6 +3587,7 @@ public void updateTime(int index, loggerExternal.exiting(getClassNameLogging(), "updateTime"); } + @Override public void updateTimestamp(int index, java.sql.Timestamp x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3895,20 +3599,7 @@ public void updateTimestamp(int index, loggerExternal.exiting(getClassNameLogging(), "updateTimestamp"); } - /** - * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current - * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param scale - * the scale of the column - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateTimestamp(int index, java.sql.Timestamp x, int scale) throws SQLServerException { @@ -3921,24 +3612,7 @@ public void updateTimestamp(int index, loggerExternal.exiting(getClassNameLogging(), "updateTimestamp"); } - /** - * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current - * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateTimestamp(int index, java.sql.Timestamp x, int scale, @@ -3952,18 +3626,7 @@ public void updateTimestamp(int index, loggerExternal.exiting(getClassNameLogging(), "updateTimestamp"); } - /** - * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current - * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateDateTime(int index, java.sql.Timestamp x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -3975,20 +3638,7 @@ public void updateDateTime(int index, loggerExternal.exiting(getClassNameLogging(), "updateDateTime"); } - /** - * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current - * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param scale - * the scale of the column - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateDateTime(int index, java.sql.Timestamp x, Integer scale) throws SQLServerException { @@ -4001,24 +3651,7 @@ public void updateDateTime(int index, loggerExternal.exiting(getClassNameLogging(), "updateDateTime"); } - /** - * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current - * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateDateTime(int index, java.sql.Timestamp x, Integer scale, @@ -4032,18 +3665,7 @@ public void updateDateTime(int index, loggerExternal.exiting(getClassNameLogging(), "updateDateTime"); } - /** - * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current - * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateSmallDateTime(int index, java.sql.Timestamp x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4055,20 +3677,7 @@ public void updateSmallDateTime(int index, loggerExternal.exiting(getClassNameLogging(), "updateSmallDateTime"); } - /** - * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current - * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param scale - * the scale of the column - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateSmallDateTime(int index, java.sql.Timestamp x, Integer scale) throws SQLServerException { @@ -4081,24 +3690,7 @@ public void updateSmallDateTime(int index, loggerExternal.exiting(getClassNameLogging(), "updateSmallDateTime"); } - /** - * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current - * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateSmallDateTime(int index, java.sql.Timestamp x, Integer scale, @@ -4112,8 +3704,9 @@ public void updateSmallDateTime(int index, loggerExternal.exiting(getClassNameLogging(), "updateSmallDateTime"); } + @Override public void updateDateTimeOffset(int index, - microsoft.sql.DateTimeOffset x) throws SQLException { + microsoft.sql.DateTimeOffset x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateDateTimeOffset", new Object[] {index, x}); @@ -4123,21 +3716,10 @@ public void updateDateTimeOffset(int index, loggerExternal.exiting(getClassNameLogging(), "updateDateTimeOffset"); } - /** - * Updates the value of the column specified to the DateTimeOffset Class value, given a zero-based column ordinal. - * - * @param index - * The zero-based ordinal of a column. - * @param x - * A DateTimeOffset Class object. - * @param scale - * scale of the column - * @throws SQLException - * when an error occurs - */ + @Override public void updateDateTimeOffset(int index, microsoft.sql.DateTimeOffset x, - Integer scale) throws SQLException { + Integer scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateDateTimeOffset", new Object[] {index, x, scale}); @@ -4147,26 +3729,11 @@ public void updateDateTimeOffset(int index, loggerExternal.exiting(getClassNameLogging(), "updateDateTimeOffset"); } - /** - * Updates the value of the column specified to the DateTimeOffset Class value, given a zero-based column ordinal. - * - * @param index - * The zero-based ordinal of a column. - * @param x - * A DateTimeOffset Class object. - * @param scale - * scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLException - * when an error occurs - */ + @Override public void updateDateTimeOffset(int index, microsoft.sql.DateTimeOffset x, Integer scale, - boolean forceEncrypt) throws SQLException { + boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateDateTimeOffset", new Object[] {index, x, scale, forceEncrypt}); @@ -4176,20 +3743,9 @@ public void updateDateTimeOffset(int index, loggerExternal.exiting(getClassNameLogging(), "updateDateTimeOffset"); } - /** - * Updates the designated column with a String value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param index - * The zero-based ordinal of a column. - * @param x - * the new column value - * @throws SQLException - * when an error occurs - */ + @Override public void updateUniqueIdentifier(int index, - String x) throws SQLException { + String x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateUniqueIdentifier", new Object[] {index, x}); @@ -4199,25 +3755,10 @@ public void updateUniqueIdentifier(int index, loggerExternal.exiting(getClassNameLogging(), "updateUniqueIdentifier"); } - /** - * Updates the designated column with a String value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param index - * The zero-based ordinal of a column. - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLException - * when an error occurs - */ + @Override public void updateUniqueIdentifier(int index, String x, - boolean forceEncrypt) throws SQLException { + boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateUniqueIdentifier", new Object[] {index, x, forceEncrypt}); @@ -4227,6 +3768,7 @@ public void updateUniqueIdentifier(int index, loggerExternal.exiting(getClassNameLogging(), "updateUniqueIdentifier"); } + @Override public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4238,8 +3780,9 @@ public void updateAsciiStream(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateAsciiStream"); } + @Override public void updateAsciiStream(int index, - java.io.InputStream x, + InputStream x, int length) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateAsciiStream", new Object[] {index, x, length}); @@ -4250,6 +3793,7 @@ public void updateAsciiStream(int index, loggerExternal.exiting(getClassNameLogging(), "updateAsciiStream"); } + @Override public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException { @@ -4261,6 +3805,7 @@ public void updateAsciiStream(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateAsciiStream"); } + @Override public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4272,8 +3817,9 @@ public void updateAsciiStream(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateAsciiStream"); } + @Override public void updateAsciiStream(java.lang.String columnName, - java.io.InputStream x, + InputStream x, int length) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateAsciiStream", new Object[] {columnName, x, length}); @@ -4284,6 +3830,7 @@ public void updateAsciiStream(java.lang.String columnName, loggerExternal.exiting(getClassNameLogging(), "updateAsciiStream"); } + @Override public void updateAsciiStream(String columnName, InputStream streamValue, long length) throws SQLException { @@ -4296,6 +3843,7 @@ public void updateAsciiStream(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateAsciiStream"); } + @Override public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4307,6 +3855,7 @@ public void updateBinaryStream(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateBinaryStream"); } + @Override public void updateBinaryStream(int columnIndex, InputStream streamValue, int length) throws SQLException { @@ -4319,6 +3868,7 @@ public void updateBinaryStream(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateBinaryStream"); } + @Override public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException { @@ -4331,6 +3881,7 @@ public void updateBinaryStream(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateBinaryStream"); } + @Override public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4342,6 +3893,7 @@ public void updateBinaryStream(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateBinaryStream"); } + @Override public void updateBinaryStream(String columnName, InputStream streamValue, int length) throws SQLException { @@ -4354,6 +3906,7 @@ public void updateBinaryStream(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateBinaryStream"); } + @Override public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException { @@ -4366,6 +3919,7 @@ public void updateBinaryStream(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateBinaryStream"); } + @Override public void updateCharacterStream(int columnIndex, Reader x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4377,6 +3931,7 @@ public void updateCharacterStream(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateCharacterStream"); } + @Override public void updateCharacterStream(int columnIndex, Reader readerValue, int length) throws SQLServerException { @@ -4389,6 +3944,7 @@ public void updateCharacterStream(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateCharacterStream"); } + @Override public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException { @@ -4401,6 +3957,7 @@ public void updateCharacterStream(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateCharacterStream"); } + @Override public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4412,6 +3969,7 @@ public void updateCharacterStream(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateCharacterStream"); } + @Override public void updateCharacterStream(String columnName, Reader readerValue, int length) throws SQLServerException { @@ -4424,6 +3982,7 @@ public void updateCharacterStream(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateCharacterStream"); } + @Override public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { @@ -4436,6 +3995,7 @@ public void updateCharacterStream(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateNCharacterStream"); } + @Override public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4447,6 +4007,7 @@ public void updateNCharacterStream(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateNCharacterStream"); } + @Override public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { @@ -4459,6 +4020,7 @@ public void updateNCharacterStream(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateNCharacterStream"); } + @Override public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4470,6 +4032,7 @@ public void updateNCharacterStream(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateNCharacterStream"); } + @Override public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { @@ -4482,6 +4045,7 @@ public void updateNCharacterStream(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateNCharacterStream"); } + @Override public void updateObject(int index, Object obj) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4493,6 +4057,7 @@ public void updateObject(int index, loggerExternal.exiting(getClassNameLogging(), "updateObject"); } + @Override public void updateObject(int index, Object x, int scale) throws SQLServerException { @@ -4505,23 +4070,7 @@ public void updateObject(int index, loggerExternal.exiting(getClassNameLogging(), "updateObject"); } - /** - * Updates the designated column with an {@code Object} value. - * - * The updater methods are used to update column values in the current row or the insert row. The updater methods do not update the underlying - * database; instead the {@code updateRow} or {@code insertRow} methods are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param precision - * the precision of the column - * @param scale - * the scale of the column - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateObject(int index, Object x, int precision, @@ -4535,27 +4084,7 @@ public void updateObject(int index, loggerExternal.exiting(getClassNameLogging(), "updateObject"); } - /** - * Updates the designated column with an {@code Object} value. - * - * The updater methods are used to update column values in the current row or the insert row. The updater methods do not update the underlying - * database; instead the {@code updateRow} or {@code insertRow} methods are called to update the database. - * - * @param index - * the first column is 1, the second is 2, ... - * @param x - * the new column value - * @param precision - * the precision of the column - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateObject(int index, Object x, int precision, @@ -4627,6 +4156,7 @@ protected final void updateObject(int index, } } + @Override public void updateNull(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "updateNull", columnName); @@ -4637,6 +4167,7 @@ public void updateNull(String columnName) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "updateNull"); } + @Override public void updateBoolean(String columnName, boolean x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4648,22 +4179,7 @@ public void updateBoolean(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateBoolean"); } - /** - * Updates the designated column with a boolean value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param columnName - * the name of the column - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * when an error occurs - */ + @Override public void updateBoolean(String columnName, boolean x, boolean forceEncrypt) throws SQLServerException { @@ -4676,6 +4192,7 @@ public void updateBoolean(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateBoolean"); } + @Override public void updateByte(String columnName, byte x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4687,23 +4204,7 @@ public void updateByte(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateByte"); } - /** - * Updates the designated column with a byte value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * - * @param columnName - * the name of the column - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateByte(String columnName, byte x, boolean forceEncrypt) throws SQLServerException { @@ -4716,6 +4217,7 @@ public void updateByte(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateByte"); } + @Override public void updateShort(String columnName, short x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4727,22 +4229,7 @@ public void updateShort(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateShort"); } - /** - * Updates the designated column with a short value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param columnName - * the name of the column - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateShort(String columnName, short x, boolean forceEncrypt) throws SQLServerException { @@ -4755,6 +4242,7 @@ public void updateShort(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateShort"); } + @Override public void updateInt(String columnName, int x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4766,22 +4254,7 @@ public void updateInt(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateInt"); } - /** - * Updates the designated column with an int value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateInt(String columnName, int x, boolean forceEncrypt) throws SQLServerException { @@ -4794,6 +4267,7 @@ public void updateInt(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateInt"); } + @Override public void updateLong(String columnName, long x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4805,22 +4279,7 @@ public void updateLong(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateLong"); } - /** - * Updates the designated column with a long value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateLong(String columnName, long x, boolean forceEncrypt) throws SQLServerException { @@ -4833,6 +4292,7 @@ public void updateLong(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateLong"); } + @Override public void updateFloat(String columnName, float x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4844,22 +4304,7 @@ public void updateFloat(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateFloat"); } - /** - * Updates the designated column with a float value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateFloat(String columnName, float x, boolean forceEncrypt) throws SQLServerException { @@ -4872,6 +4317,7 @@ public void updateFloat(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateFloat"); } + @Override public void updateDouble(String columnName, double x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4883,22 +4329,7 @@ public void updateDouble(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateDouble"); } - /** - * Updates the designated column with a double value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateDouble(String columnName, double x, boolean forceEncrypt) throws SQLServerException { @@ -4911,6 +4342,7 @@ public void updateDouble(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateDouble"); } + @Override public void updateBigDecimal(String columnName, BigDecimal x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -4922,22 +4354,7 @@ public void updateBigDecimal(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateBigDecimal"); } - /** - * Updates the designated column with a java.sql.BigDecimal value. The updater methods are used to update column values in the - * current row or the insert row. The updater methods do not update the underlying database; instead the updateRow or - * insertRow methods are called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateBigDecimal(String columnName, BigDecimal x, boolean forceEncrypt) throws SQLServerException { @@ -4950,23 +4367,7 @@ public void updateBigDecimal(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateBigDecimal"); } - /** - * Updates the designated column with a java.sql.BigDecimal value. The updater methods are used to update column values in the - * current row or the insert row. The updater methods do not update the underlying database; instead the updateRow or - * insertRow methods are called to update the database. - * - * @param columnName - * is the name of the column and Always Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set - * to false, the driver will not force encryption on parameters. - * @param x - * BigDecimal value - * @param precision - * the precision of the column - * @param scale - * the scale of the column - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateBigDecimal(String columnName, BigDecimal x, Integer precision, @@ -4980,27 +4381,7 @@ public void updateBigDecimal(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateBigDecimal"); } - /** - * Updates the designated column with a java.sql.BigDecimal value. The updater methods are used to update column values in the - * current row or the insert row. The updater methods do not update the underlying database; instead the updateRow or - * insertRow methods are called to update the database. - * - * @param columnName - * is the name of the column and Always Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set - * to false, the driver will not force encryption on parameters. - * @param x - * BigDecimal value - * @param precision - * the precision of the column - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateBigDecimal(String columnName, BigDecimal x, Integer precision, @@ -5015,6 +4396,7 @@ public void updateBigDecimal(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateBigDecimal"); } + @Override public void updateString(String columnName, String x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -5026,22 +4408,7 @@ public void updateString(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateString"); } - /** - * Updates the designated column with a String value. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateString(String columnName, String x, boolean forceEncrypt) throws SQLServerException { @@ -5054,6 +4421,7 @@ public void updateString(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateString"); } + @Override public void updateBytes(String columnName, byte x[]) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -5065,23 +4433,7 @@ public void updateBytes(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateBytes"); } - /** - * Updates the designated column with a byte array value. - * - * The updater methods are used to update column values in the current row or the insert row. The updater methods do not update the underlying - * database; instead the updateRow or insertRow methods are called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateBytes(String columnName, byte x[], boolean forceEncrypt) throws SQLServerException { @@ -5094,6 +4446,7 @@ public void updateBytes(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateBytes"); } + @Override public void updateDate(String columnName, java.sql.Date x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -5105,22 +4458,7 @@ public void updateDate(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateDate"); } - /** - * Updates the designated column with a java.sql.Date value. The updater methods are used to update column values in the current row - * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateDate(String columnName, java.sql.Date x, boolean forceEncrypt) throws SQLServerException { @@ -5133,6 +4471,7 @@ public void updateDate(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateDate"); } + @Override public void updateTime(String columnName, java.sql.Time x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -5144,20 +4483,7 @@ public void updateTime(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateTime"); } - /** - * Updates the designated column with a java.sql.Time value. The updater methods are used to update column values in the current row - * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @param scale - * the scale of the column - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateTime(String columnName, java.sql.Time x, int scale) throws SQLServerException { @@ -5170,24 +4496,7 @@ public void updateTime(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateTime"); } - /** - * Updates the designated column with a java.sql.Time value. The updater methods are used to update column values in the current row - * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateTime(String columnName, java.sql.Time x, int scale, @@ -5201,6 +4510,7 @@ public void updateTime(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateTime"); } + @Override public void updateTimestamp(String columnName, java.sql.Timestamp x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -5212,20 +4522,7 @@ public void updateTimestamp(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateTimestamp"); } - /** - * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current - * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @param scale - * the scale of the column - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateTimestamp(String columnName, java.sql.Timestamp x, int scale) throws SQLServerException { @@ -5238,24 +4535,7 @@ public void updateTimestamp(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateTimestamp"); } - /** - * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current - * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateTimestamp(String columnName, java.sql.Timestamp x, int scale, @@ -5269,18 +4549,7 @@ public void updateTimestamp(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateTimestamp"); } - /** - * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current - * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateDateTime(String columnName, java.sql.Timestamp x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -5292,20 +4561,7 @@ public void updateDateTime(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateDateTime"); } - /** - * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current - * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @param scale - * the scale of the column - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateDateTime(String columnName, java.sql.Timestamp x, int scale) throws SQLServerException { @@ -5318,24 +4574,7 @@ public void updateDateTime(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateDateTime"); } - /** - * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current - * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateDateTime(String columnName, java.sql.Timestamp x, int scale, @@ -5349,18 +4588,7 @@ public void updateDateTime(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateDateTime"); } - /** - * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current - * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateSmallDateTime(String columnName, java.sql.Timestamp x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -5372,20 +4600,7 @@ public void updateSmallDateTime(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateSmallDateTime"); } - /** - * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current - * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @param scale - * the scale of the column - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateSmallDateTime(String columnName, java.sql.Timestamp x, int scale) throws SQLServerException { @@ -5398,24 +4613,7 @@ public void updateSmallDateTime(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateSmallDateTime"); } - /** - * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current - * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow - * methods are called to update the database. - * - * @param columnName - * is the name of the column - * @param x - * the new column value - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateSmallDateTime(String columnName, java.sql.Timestamp x, int scale, @@ -5429,8 +4627,9 @@ public void updateSmallDateTime(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateSmallDateTime"); } + @Override public void updateDateTimeOffset(String columnName, - microsoft.sql.DateTimeOffset x) throws SQLException { + microsoft.sql.DateTimeOffset x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateDateTimeOffset", new Object[] {columnName, x}); @@ -5440,21 +4639,10 @@ public void updateDateTimeOffset(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateDateTimeOffset"); } - /** - * Updates the value of the column specified to the DateTimeOffset Class value, given a column name. - * - * @param columnName - * The name of a column. - * @param x - * A DateTimeOffset Class object. - * @param scale - * the scale of the column - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateDateTimeOffset(String columnName, microsoft.sql.DateTimeOffset x, - int scale) throws SQLException { + int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateDateTimeOffset", new Object[] {columnName, x, scale}); @@ -5464,26 +4652,11 @@ public void updateDateTimeOffset(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateDateTimeOffset"); } - /** - * Updates the value of the column specified to the DateTimeOffset Class value, given a column name. - * - * @param columnName - * The name of a column. - * @param x - * A DateTimeOffset Class object. - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLException - * If any errors occur. - */ + @Override public void updateDateTimeOffset(String columnName, microsoft.sql.DateTimeOffset x, int scale, - boolean forceEncrypt) throws SQLException { + boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateDateTimeOffset", new Object[] {columnName, x, scale, forceEncrypt}); @@ -5493,20 +4666,9 @@ public void updateDateTimeOffset(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateDateTimeOffset"); } - /** - * Updates the designated column with a Stringvalue. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param columnName - * The name of a column. - * @param x - * the new column value - * @throws SQLException - * If any errors occur. - */ + @Override public void updateUniqueIdentifier(String columnName, - String x) throws SQLException { + String x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateUniqueIdentifier", new Object[] {columnName, x}); @@ -5516,25 +4678,10 @@ public void updateUniqueIdentifier(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateUniqueIdentifier"); } - /** - * Updates the designated column with a Stringvalue. The updater methods are used to update column values in the current row or the - * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are - * called to update the database. - * - * @param columnName - * The name of a column. - * @param x - * the new column value - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLException - * If any errors occur. - */ + @Override public void updateUniqueIdentifier(String columnName, String x, - boolean forceEncrypt) throws SQLException { + boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateUniqueIdentifier", new Object[] {columnName, x, forceEncrypt}); @@ -5544,6 +4691,7 @@ public void updateUniqueIdentifier(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateUniqueIdentifier"); } + @Override public void updateObject(String columnName, Object x, int scale) throws SQLServerException { @@ -5556,23 +4704,7 @@ public void updateObject(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateObject"); } - /** - * Updates the designated column with an {@code Object} value. - * - * The updater methods are used to update column values in the current row or the insert row. The updater methods do not update the underlying - * database; instead the {@code updateRow} or {@code insertRow} methods are called to update the database. - * - * @param columnName - * The name of a column. - * @param x - * the new column value - * @param precision - * the precision of the column - * @param scale - * the scale of the column - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateObject(String columnName, Object x, int precision, @@ -5586,27 +4718,7 @@ public void updateObject(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateObject"); } - /** - * Updates the designated column with an {@code Object} value. - * - * The updater methods are used to update column values in the current row or the insert row. The updater methods do not update the underlying - * database; instead the {@code updateRow} or {@code insertRow} methods are called to update the database. - * - * @param columnName - * The name of a column. - * @param x - * the new column value - * @param precision - * the precision of the column - * @param scale - * the scale of the column - * @param forceEncrypt - * If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always - * Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force - * encryption on parameters. - * @throws SQLServerException - * If any errors occur. - */ + @Override public void updateObject(String columnName, Object x, int precision, @@ -5621,6 +4733,7 @@ public void updateObject(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateObject"); } + @Override public void updateObject(String columnName, Object x) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -5632,18 +4745,19 @@ public void updateObject(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateObject"); } + @Override public void updateRowId(int columnIndex, RowId x) throws SQLException { - // Not implemented - throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + SQLServerException.throwNotSupportedException(stmt.connection, stmt); } + @Override public void updateRowId(String columnLabel, RowId x) throws SQLException { - // Not implemented - throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + SQLServerException.throwNotSupportedException(stmt.connection, stmt); } + @Override public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -5652,6 +4766,7 @@ public void updateSQLXML(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateSQLXML"); } + @Override public void updateSQLXML(String columnLabel, SQLXML x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -5660,6 +4775,7 @@ public void updateSQLXML(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateSQLXML"); } + @Override public int getHoldability() throws SQLException { loggerExternal.entering(getClassNameLogging(), "getHoldability"); @@ -5683,7 +4799,8 @@ public int getHoldability() throws SQLException { /* ----------------------- Update result set ------------------------- */ - public void insertRow() throws SQLServerException { + @Override + public void insertRow() throws SQLException { loggerExternal.entering(getClassNameLogging(), "insertRow"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); @@ -5774,7 +4891,8 @@ private void doInsertRowRPC(TDSCommand command, if (hasUpdatedColumns()) { tdsWriter.writeRPCStringUnicode(tableName); - for (Column column : columns) column.sendByRPC(tdsWriter, stmt.connection); + for (Column column : columns) + column.sendByRPC(tdsWriter, stmt.connection); } else { tdsWriter.writeRPCStringUnicode(""); @@ -5784,7 +4902,8 @@ private void doInsertRowRPC(TDSCommand command, TDSParser.parse(command.startResponse(), command.getLogContext()); } - public void updateRow() throws SQLServerException { + @Override + public void updateRow() throws SQLException { loggerExternal.entering(getClassNameLogging(), "updateRow"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); @@ -5848,7 +4967,8 @@ private void doUpdateRowRPC(TDSCommand command) throws SQLServerException { assert hasUpdatedColumns(); - for (Column column : columns) column.sendByRPC(tdsWriter, stmt.connection); + for (Column column : columns) + column.sendByRPC(tdsWriter, stmt.connection); TDSParser.parse(command.startResponse(), command.getLogContext()); } @@ -5862,7 +4982,8 @@ final boolean hasUpdatedColumns() { return false; } - public void deleteRow() throws SQLServerException { + @Override + public void deleteRow() throws SQLException { loggerExternal.entering(getClassNameLogging(), "deleteRow"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); @@ -5921,7 +5042,8 @@ private void doDeleteRowRPC(TDSCommand command) throws SQLServerException { TDSParser.parse(command.startResponse(), command.getLogContext()); } - public void refreshRow() throws SQLServerException { + @Override + public void refreshRow() throws SQLException { loggerExternal.entering(getClassNameLogging(), "refreshRow"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); @@ -6000,6 +5122,7 @@ private void cancelUpdates() { clearColumnsValues(); } + @Override public void cancelRowUpdates() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "cancelRowUpdates"); checkClosed(); @@ -6014,6 +5137,7 @@ public void cancelRowUpdates() throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "cancelRowUpdates"); } + @Override public void moveToInsertRow() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "moveToInsertRow"); if (logger.isLoggable(java.util.logging.Level.FINER)) @@ -6030,6 +5154,7 @@ public void moveToInsertRow() throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "moveToInsertRow"); } + @Override public void moveToCurrentRow() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "moveToCurrentRow"); if (logger.isLoggable(java.util.logging.Level.FINER)) @@ -6050,7 +5175,8 @@ public void moveToCurrentRow() throws SQLServerException { } - /* L0 */ public java.sql.Statement getStatement() throws SQLServerException { + @Override + public java.sql.Statement getStatement() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getStatement"); checkClosed(); loggerExternal.exiting(getClassNameLogging(), "getStatement", stmt); @@ -6059,6 +5185,7 @@ public void moveToCurrentRow() throws SQLServerException { /* JDBC 3.0 */ + @Override public void updateClob(int columnIndex, Clob clobValue) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -6070,6 +5197,7 @@ public void updateClob(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateClob"); } + @Override public void updateClob(int columnIndex, Reader reader) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -6081,6 +5209,7 @@ public void updateClob(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateClob"); } + @Override public void updateClob(int columnIndex, Reader reader, long length) throws SQLException { @@ -6093,6 +5222,7 @@ public void updateClob(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateClob"); } + @Override public void updateClob(String columnName, Clob clobValue) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -6104,6 +5234,7 @@ public void updateClob(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateClob"); } + @Override public void updateClob(String columnLabel, Reader reader) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -6115,6 +5246,7 @@ public void updateClob(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateClob"); } + @Override public void updateClob(String columnLabel, Reader reader, long length) throws SQLException { @@ -6127,6 +5259,7 @@ public void updateClob(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateClob"); } + @Override public void updateNClob(int columnIndex, NClob nClob) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -6138,6 +5271,7 @@ public void updateNClob(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateNClob"); } + @Override public void updateNClob(int columnIndex, Reader reader) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -6149,6 +5283,7 @@ public void updateNClob(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateNClob"); } + @Override public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException { @@ -6161,6 +5296,7 @@ public void updateNClob(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateNClob"); } + @Override public void updateNClob(String columnLabel, NClob nClob) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -6172,6 +5308,7 @@ public void updateNClob(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateNClob"); } + @Override public void updateNClob(String columnLabel, Reader reader) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -6183,6 +5320,7 @@ public void updateNClob(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateNClob"); } + @Override public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException { @@ -6195,6 +5333,7 @@ public void updateNClob(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateNClob"); } + @Override public void updateBlob(int columnIndex, Blob blobValue) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -6206,6 +5345,7 @@ public void updateBlob(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateBlob"); } + @Override public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -6217,6 +5357,7 @@ public void updateBlob(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateBlob"); } + @Override public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException { @@ -6229,6 +5370,7 @@ public void updateBlob(int columnIndex, loggerExternal.exiting(getClassNameLogging(), "updateBlob"); } + @Override public void updateBlob(String columnName, Blob blobValue) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -6240,6 +5382,7 @@ public void updateBlob(String columnName, loggerExternal.exiting(getClassNameLogging(), "updateBlob"); } + @Override public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -6251,6 +5394,7 @@ public void updateBlob(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateBlob"); } + @Override public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException { @@ -6263,33 +5407,39 @@ public void updateBlob(String columnLabel, loggerExternal.exiting(getClassNameLogging(), "updateBlob"); } - /* L3 */ public void updateArray(int columnIndex, - Array x) throws SQLServerException { - stmt.NotImplemented(); + @Override + public void updateArray(int columnIndex, + Array x) throws SQLException { + SQLServerException.throwNotSupportedException(stmt.connection, stmt); } - /* L3 */ public void updateArray(java.lang.String columnName, - Array x) throws SQLServerException { - stmt.NotImplemented(); + @Override + public void updateArray(java.lang.String columnName, + Array x) throws SQLException { + SQLServerException.throwNotSupportedException(stmt.connection, stmt); } - /* L3 */ public void updateRef(int columnIndex, - Ref x) throws SQLServerException { - stmt.NotImplemented(); + @Override + public void updateRef(int columnIndex, + Ref x) throws SQLException { + SQLServerException.throwNotSupportedException(stmt.connection, stmt); } - /* L3 */ public void updateRef(java.lang.String columnName, - Ref x) throws SQLServerException { - stmt.NotImplemented(); + @Override + public void updateRef(java.lang.String columnName, + Ref x) throws SQLException { + SQLServerException.throwNotSupportedException(stmt.connection, stmt); } - /* L3 */ public java.net.URL getURL(int columnIndex) throws SQLServerException { - stmt.NotImplemented(); + @Override + public java.net.URL getURL(int columnIndex) throws SQLException { + SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } - /* L3 */ public java.net.URL getURL(String sColumn) throws SQLServerException { - stmt.NotImplemented(); + @Override + public java.net.URL getURL(String sColumn) throws SQLException { + SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } @@ -6353,16 +5503,16 @@ boolean onDone(TDSReader tdsReader) throws SQLServerException { int token = tdsReader.peekTokenType(); StreamDone doneToken = new StreamDone(); doneToken.setFromTDS(tdsReader); - + int packetType = tdsReader.peekTokenType(); if (-1 != packetType && TDS.TDS_DONEINPROC == token) { - switch (packetType) { - case TDS.TDS_ENV_CHG: - case TDS.TDS_ERR: - return true; - default: - break; - } + switch (packetType) { + case TDS.TDS_ENV_CHG: + case TDS.TDS_ERR: + return true; + default: + break; + } } // Done with all the rows in this fetch buffer and done with parsing @@ -6573,24 +5723,26 @@ final void doServerFetch(int fetchType, scrollWindow.reset(); } } - + /* - * Checks for any LOBs which need to be available after the RS is closed, and loads their contents from stream into memory. - * Closed LOBs will not be populated. + * Checks for any LOBs which need to be available after the RS is closed, and loads their contents from stream into memory. Closed LOBs will not + * be populated. */ private void fillLOBs() { - if (null != activeLOB) { - try { - activeLOB.fillFromStream(); - } catch (SQLException e) { - if (logger.isLoggable(java.util.logging.Level.FINER)) { - logger.finer(toString() + "Filling Lobs before closing: " + e.getMessage()); - } - } finally { - activeLOB = null; - } - } - } + if (null != activeLOB) { + try { + activeLOB.fillFromStream(); + } + catch (SQLException e) { + if (logger.isLoggable(java.util.logging.Level.FINER)) { + logger.finer(toString() + "Filling Lobs before closing: " + e.getMessage()); + } + } + finally { + activeLOB = null; + } + } + } /** * Discards the contents of the current fetch buffer. @@ -6603,8 +5755,8 @@ private void fillLOBs() { * fetch buffer is considered to be discarded. */ private void discardFetchBuffer() { - //fills blobs before discarding anything - fillLOBs(); + // fills blobs before discarding anything + fillLOBs(); // Clear the TDSReader mark at the start of the fetch buffer fetchBuffer.clearStartMark(); @@ -6674,4 +5826,104 @@ final boolean doExecute() throws SQLServerException { logger.finer(toString() + " Closed cursor:" + serverCursorId); } } + + @Override + public void updateObject(int index, + Object obj, + SQLType targetSqlType) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {index, obj, targetSqlType}); + + checkClosed(); + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + updateObject(index, obj, null, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, false); + + loggerExternal.exiting(getClassNameLogging(), "updateObject"); + } + + @Override + public void updateObject(int index, + Object obj, + SQLType targetSqlType, + int scale) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {index, obj, targetSqlType, scale}); + + checkClosed(); + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + updateObject(index, obj, scale, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, false); + + loggerExternal.exiting(getClassNameLogging(), "updateObject"); + } + + @Override + public void updateObject(int index, + Object obj, + SQLType targetSqlType, + int scale, + boolean forceEncrypt) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {index, obj, targetSqlType, scale, forceEncrypt}); + + checkClosed(); + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + updateObject(index, obj, scale, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, forceEncrypt); + + loggerExternal.exiting(getClassNameLogging(), "updateObject"); + } + + @Override + public void updateObject(String columnName, + Object obj, + SQLType targetSqlType, + int scale) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {columnName, obj, targetSqlType, scale}); + + checkClosed(); + + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + updateObject(findColumn(columnName), obj, scale, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, false); + + loggerExternal.exiting(getClassNameLogging(), "updateObject"); + } + + @Override + public void updateObject(String columnName, + Object obj, + SQLType targetSqlType, + int scale, + boolean forceEncrypt) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {columnName, obj, targetSqlType, scale, forceEncrypt}); + + checkClosed(); + + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + updateObject(findColumn(columnName), obj, scale, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, forceEncrypt); + + loggerExternal.exiting(getClassNameLogging(), "updateObject"); + } + + @Override + public void updateObject(String columnName, + Object obj, + SQLType targetSqlType) throws SQLServerException { + + if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) + loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {columnName, obj, targetSqlType}); + + checkClosed(); + + // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types + updateObject(findColumn(columnName), obj, null, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, false); + + loggerExternal.exiting(getClassNameLogging(), "updateObject"); + } + } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet42.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet42.java deleted file mode 100644 index cc900e65f..000000000 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet42.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.jdbc; - -import java.sql.SQLType; - -/** - * - * This class is separated from SQLServerResultSet class in order to resolve compiling error of missing Java 8 Types when running with Java 7. - * - * This class will be initialized instead of SQLServerResultSet when Java 8 and JDBC 4.2 are used. - * - */ -public class SQLServerResultSet42 extends SQLServerResultSet implements ISQLServerResultSet42 { - - /** - * Makes a new result set - * - * @param stmtIn - * the generating statement - * @throws SQLServerException - * when an error occurs - */ - public SQLServerResultSet42(SQLServerStatement stmtIn) throws SQLServerException { - super(stmtIn); - } - - public void updateObject(int index, - Object obj, - SQLType targetSqlType) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {index, obj, targetSqlType}); - - checkClosed(); - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - updateObject(index, obj, null, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, false); - - loggerExternal.exiting(getClassNameLogging(), "updateObject"); - } - - public void updateObject(int index, - Object obj, - SQLType targetSqlType, - int scale) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {index, obj, targetSqlType, scale}); - - checkClosed(); - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - updateObject(index, obj, scale, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, false); - - loggerExternal.exiting(getClassNameLogging(), "updateObject"); - } - - public void updateObject(int index, - Object obj, - SQLType targetSqlType, - int scale, - boolean forceEncrypt) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {index, obj, targetSqlType, scale, forceEncrypt}); - - checkClosed(); - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - updateObject(index, obj, scale, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, forceEncrypt); - - loggerExternal.exiting(getClassNameLogging(), "updateObject"); - } - - public void updateObject(String columnName, - Object obj, - SQLType targetSqlType, - int scale) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {columnName, obj, targetSqlType, scale}); - - checkClosed(); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - updateObject(findColumn(columnName), obj, scale, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, false); - - loggerExternal.exiting(getClassNameLogging(), "updateObject"); - } - - public void updateObject(String columnName, - Object obj, - SQLType targetSqlType, - int scale, - boolean forceEncrypt) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {columnName, obj, targetSqlType, scale, forceEncrypt}); - - checkClosed(); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - updateObject(findColumn(columnName), obj, scale, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, forceEncrypt); - - loggerExternal.exiting(getClassNameLogging(), "updateObject"); - } - - public void updateObject(String columnName, - Object obj, - SQLType targetSqlType) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); - - if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {columnName, obj, targetSqlType}); - - checkClosed(); - - // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - updateObject(findColumn(columnName), obj, null, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, false); - - loggerExternal.exiting(getClassNameLogging(), "updateObject"); - } - -} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSetMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSetMetaData.java index c9aa686a6..e3b7679fa 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSetMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSetMetaData.java @@ -18,7 +18,7 @@ * details. */ -public final class SQLServerResultSetMetaData implements java.sql.ResultSetMetaData { +public final class SQLServerResultSetMetaData implements ISQLServerResultSetMetaData { private SQLServerConnection con; private final SQLServerResultSet rs; static final private java.util.logging.Logger logger = java.util.logging.Logger @@ -44,7 +44,7 @@ final public String toString() { * @param rs * the parent result set */ - /* L0 */ SQLServerResultSetMetaData(SQLServerConnection con, + SQLServerResultSetMetaData(SQLServerConnection con, SQLServerResultSet rs) { traceID = " SQLServerResultSetMetaData:" + nextInstanceID(); this.con = con; @@ -57,11 +57,13 @@ final public String toString() { /* ------------------ JDBC API Methods --------------------- */ + @Override public boolean isWrapperFor(Class iface) throws SQLException { boolean f = iface.isInstance(this); return f; } + @Override public T unwrap(Class iface) throws SQLException { T t; try { @@ -73,14 +75,17 @@ public T unwrap(Class iface) throws SQLException { return t; } + @Override public String getCatalogName(int column) throws SQLServerException { return rs.getColumn(column).getTableName().getDatabaseName(); } - /* L0 */ public int getColumnCount() throws SQLServerException { + @Override + public int getColumnCount() throws SQLServerException { return rs.getColumnCount(); } + @Override public int getColumnDisplaySize(int column) throws SQLServerException { CryptoMetadata cryptoMetadata = rs.getColumn(column).getCryptoMetadata(); if (null != cryptoMetadata) { @@ -90,14 +95,17 @@ public int getColumnDisplaySize(int column) throws SQLServerException { return rs.getColumn(column).getTypeInfo().getDisplaySize(); } + @Override public String getColumnLabel(int column) throws SQLServerException { return rs.getColumn(column).getColumnName(); } + @Override public String getColumnName(int column) throws SQLServerException { return rs.getColumn(column).getColumnName(); } + @Override public int getColumnType(int column) throws SQLServerException { // under Katmai map the max types to non max to be inline with DBMD. TypeInfo typeInfo = rs.getColumn(column).getTypeInfo(); @@ -152,6 +160,7 @@ public int getColumnType(int column) throws SQLServerException { return r; } + @Override public String getColumnTypeName(int column) throws SQLServerException { CryptoMetadata cryptoMetadata = rs.getColumn(column).getCryptoMetadata(); if (null != cryptoMetadata) { @@ -161,6 +170,7 @@ public String getColumnTypeName(int column) throws SQLServerException { return rs.getColumn(column).getTypeInfo().getSSTypeName(); } + @Override public int getPrecision(int column) throws SQLServerException { CryptoMetadata cryptoMetadata = rs.getColumn(column).getCryptoMetadata(); if (null != cryptoMetadata) { @@ -170,6 +180,7 @@ public int getPrecision(int column) throws SQLServerException { return rs.getColumn(column).getTypeInfo().getPrecision(); } + @Override public int getScale(int column) throws SQLServerException { CryptoMetadata cryptoMetadata = rs.getColumn(column).getCryptoMetadata(); if (null != cryptoMetadata) { @@ -179,14 +190,17 @@ public int getScale(int column) throws SQLServerException { return rs.getColumn(column).getTypeInfo().getScale(); } + @Override public String getSchemaName(int column) throws SQLServerException { return rs.getColumn(column).getTableName().getSchemaName(); } + @Override public String getTableName(int column) throws SQLServerException { return rs.getColumn(column).getTableName().getObjectName(); } + @Override public boolean isAutoIncrement(int column) throws SQLServerException { CryptoMetadata cryptoMetadata = rs.getColumn(column).getCryptoMetadata(); if (null != cryptoMetadata) { @@ -196,6 +210,7 @@ public boolean isAutoIncrement(int column) throws SQLServerException { return rs.getColumn(column).getTypeInfo().isIdentity(); } + @Override public boolean isCaseSensitive(int column) throws SQLServerException { CryptoMetadata cryptoMetadata = rs.getColumn(column).getCryptoMetadata(); if (null != cryptoMetadata) { @@ -205,6 +220,7 @@ public boolean isCaseSensitive(int column) throws SQLServerException { return rs.getColumn(column).getTypeInfo().isCaseSensitive(); } + @Override public boolean isCurrency(int column) throws SQLServerException { SSType ssType = rs.getColumn(column).getTypeInfo().getSSType(); @@ -216,6 +232,7 @@ public boolean isCurrency(int column) throws SQLServerException { return SSType.MONEY == ssType || SSType.SMALLMONEY == ssType; } + @Override public boolean isDefinitelyWritable(int column) throws SQLServerException { CryptoMetadata cryptoMetadata = rs.getColumn(column).getCryptoMetadata(); if (null != cryptoMetadata) { @@ -225,6 +242,7 @@ public boolean isDefinitelyWritable(int column) throws SQLServerException { return TypeInfo.UPDATABLE_READ_WRITE == rs.getColumn(column).getTypeInfo().getUpdatability(); } + @Override public int isNullable(int column) throws SQLServerException { CryptoMetadata cryptoMetadata = rs.getColumn(column).getCryptoMetadata(); if (null != cryptoMetadata) { @@ -234,6 +252,7 @@ public int isNullable(int column) throws SQLServerException { return rs.getColumn(column).getTypeInfo().isNullable() ? columnNullable : columnNoNulls; } + @Override public boolean isReadOnly(int column) throws SQLServerException { CryptoMetadata cryptoMetadata = rs.getColumn(column).getCryptoMetadata(); if (null != cryptoMetadata) { @@ -243,6 +262,7 @@ public boolean isReadOnly(int column) throws SQLServerException { return TypeInfo.UPDATABLE_READ_ONLY == rs.getColumn(column).getTypeInfo().getUpdatability(); } + @Override public boolean isSearchable(int column) throws SQLServerException { SSType ssType = null; CryptoMetadata cryptoMetadata = rs.getColumn(column).getCryptoMetadata(); @@ -267,6 +287,7 @@ public boolean isSearchable(int column) throws SQLServerException { } } + @Override public boolean isSigned(int column) throws SQLServerException { CryptoMetadata cryptoMetadata = rs.getColumn(column).getCryptoMetadata(); if (null != cryptoMetadata) { @@ -276,15 +297,7 @@ public boolean isSigned(int column) throws SQLServerException { return rs.getColumn(column).getTypeInfo().getSSType().getJDBCType().isSigned(); } - /** - * Returns true if the column is a SQLServer SparseColumnSet - * - * @param column - * The column number - * @return true if a column in a result set is a sparse column set, otherwise false. - * @throws SQLServerException - * when an error occurs - */ + @Override public boolean isSparseColumnSet(int column) throws SQLServerException { CryptoMetadata cryptoMetadata = rs.getColumn(column).getCryptoMetadata(); if (null != cryptoMetadata) { @@ -294,6 +307,7 @@ public boolean isSparseColumnSet(int column) throws SQLServerException { return rs.getColumn(column).getTypeInfo().isSparseColumnSet(); } + @Override public boolean isWritable(int column) throws SQLServerException { int updatability = -1; CryptoMetadata cryptoMetadata = rs.getColumn(column).getCryptoMetadata(); @@ -306,6 +320,7 @@ public boolean isWritable(int column) throws SQLServerException { return TypeInfo.UPDATABLE_READ_WRITE == updatability || TypeInfo.UPDATABLE_UNKNOWN == updatability; } + @Override public String getColumnClassName(int column) throws SQLServerException { CryptoMetadata cryptoMetadata = rs.getColumn(column).getCryptoMetadata(); if (null != cryptoMetadata) { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSQLXML.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSQLXML.java index 3e99ab41f..da860456a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSQLXML.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSQLXML.java @@ -158,7 +158,8 @@ else if (null != docValue) { InputStream getStream() { return contents; } - + + @Override public void free() throws SQLException { if (!isFreed) { isFreed = true; @@ -209,6 +210,7 @@ void checkWriteXML() throws SQLException { * when an error occurs * @return the input stream to that contains the SQLXML data */ + @Override public InputStream getBinaryStream() throws SQLException { checkClosed(); checkReadXML(); @@ -224,6 +226,7 @@ public InputStream getBinaryStream() throws SQLException { * when an error occurs * @return OutputStream */ + @Override public java.io.OutputStream setBinaryStream() throws SQLException { checkClosed(); checkWriteXML(); @@ -232,6 +235,7 @@ public java.io.OutputStream setBinaryStream() throws SQLException { return outputStreamValue; } + @Override public java.io.Writer setCharacterStream() throws SQLException { checkClosed(); checkWriteXML(); @@ -240,6 +244,7 @@ public java.io.Writer setCharacterStream() throws SQLException { return new OutputStreamWriter(outputStreamValue, Encoding.UNICODE.charset()); } + @Override public Reader getCharacterStream() throws SQLException { checkClosed(); checkReadXML(); @@ -261,6 +266,7 @@ public Reader getCharacterStream() throws SQLException { return rd; } + @Override public String getString() throws SQLException { checkClosed(); checkReadXML(); @@ -279,6 +285,7 @@ public String getString() throws SQLException { return new String(byteContents, 0, byteContents.length, Encoding.UNICODE.charset()); } + @Override public void setString(String value) throws SQLException { checkClosed(); checkWriteXML(); @@ -289,6 +296,7 @@ public void setString(String value) throws SQLException { } // Support the following DOMSource, SAXSource, StAX and Stream. Also, null means default which is stream source + @Override public T getSource(Class iface) throws SQLException { checkClosed(); checkReadXML(); @@ -324,6 +332,7 @@ else if (StreamSource.class == iface) { return src; } + @Override public T setResult(Class resultClass) throws SQLException { checkClosed(); checkWriteXML(); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSavepoint.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSavepoint.java index 9c0b62ed8..0d10816f5 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSavepoint.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSavepoint.java @@ -8,7 +8,6 @@ package com.microsoft.sqlserver.jdbc; -import java.sql.Savepoint; import java.text.MessageFormat; /** @@ -19,7 +18,7 @@ * details. */ -public final class SQLServerSavepoint implements Savepoint { +public final class SQLServerSavepoint implements ISQLServerSavepoint { private final String sName; private final int nId; private final SQLServerConnection con; @@ -45,17 +44,14 @@ public SQLServerSavepoint(SQLServerConnection con, } } + @Override public String getSavepointName() throws SQLServerException { if (sName == null) SQLServerException.makeFromDriverError(con, null, SQLServerException.getErrString("R_savepointNotNamed"), null, false); return sName; } - /** - * Get the savepoint label - * - * @return the name - */ + @Override public String getLabel() { if (sName == null) return "S" + nId; @@ -63,15 +59,12 @@ public String getLabel() { return sName; } - /** - * Checks if the savepoint label is null - * - * @return true is the savepoint is named. Otherwise, false. - */ + @Override public boolean isNamed() { return sName != null; } + @Override public int getSavepointId() throws SQLServerException { if (sName != null) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_savepointNamed")); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSpatialDatatype.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSpatialDatatype.java index 3fe57e8dd..4c75590c3 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSpatialDatatype.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSpatialDatatype.java @@ -16,10 +16,12 @@ import java.util.Locale; abstract class SQLServerSpatialDatatype { - - /**WKT = Well-Known-Text, WKB = Well-Knwon-Binary */ - /**As a general rule, the ~IndexEnd variables are non-inclusive (i.e. pointIndexEnd = 8 means the shape using it will - * only go up to the 7th index of the array) */ + + /** WKT = Well-Known-Text, WKB = Well-Knwon-Binary */ + /** + * As a general rule, the ~IndexEnd variables are non-inclusive (i.e. pointIndexEnd = 8 means the shape using it will only go up to the 7th index + * of the array) + */ protected ByteBuffer buffer; protected InternalSpatialDatatype internalType; protected String wkt; @@ -45,7 +47,7 @@ abstract class SQLServerSpatialDatatype { protected Shape shapes[]; protected Segment segments[]; - //serialization properties + // serialization properties protected boolean hasZvalues = false; protected boolean hasMvalues = false; protected boolean isValid = false; @@ -53,16 +55,16 @@ abstract class SQLServerSpatialDatatype { protected boolean isSingleLineSegment = false; protected boolean isLargerThanHemisphere = false; protected boolean isNull = true; - + protected final byte FA_INTERIOR_RING = 0; protected final byte FA_STROKE = 1; protected final byte FA_EXTERIOR_RING = 2; - + protected final byte FA_POINT = 0; protected final byte FA_LINE = 1; protected final byte FA_ARC = 2; protected final byte FA_COMPOSITE_CURVE = 3; - + // WKT to WKB properties protected int currentWktPos = 0; protected List pointList = new ArrayList(); @@ -70,54 +72,67 @@ abstract class SQLServerSpatialDatatype { protected List shapeList = new ArrayList(); protected List segmentList = new ArrayList(); protected byte serializationProperties = 0; - + private final byte SEGMENT_LINE = 0; private final byte SEGMENT_ARC = 1; private final byte SEGMENT_FIRST_LINE = 2; private final byte SEGMENT_FIRST_ARC = 3; - - private final byte hasZvaluesMask = 0b00000001; - private final byte hasMvaluesMask = 0b00000010; - private final byte isValidMask = 0b00000100; - private final byte isSinglePointMask = 0b00001000; - private final byte isSingleLineSegmentMask = 0b00010000; - private final byte isLargerThanHemisphereMask = 0b00100000; - + + private final byte hasZvaluesMask = 0b00000001; + private final byte hasMvaluesMask = 0b00000010; + private final byte isValidMask = 0b00000100; + private final byte isSinglePointMask = 0b00001000; + private final byte isSingleLineSegmentMask = 0b00010000; + private final byte isLargerThanHemisphereMask = 0b00100000; + private List version_one_shape_indexes = new ArrayList(); - + /** * Serializes the Geogemetry/Geography instance to WKB. * - * @param noZM flag to indicate if Z and M coordinates should be included + * @param noZM + * flag to indicate if Z and M coordinates should be included */ protected abstract void serializeToWkb(boolean noZM); - + /** - * Deserialize the buffer (that contains WKB representation of Geometry/Geography data), and stores it - * into multiple corresponding data structures. + * Deserialize the buffer (that contains WKB representation of Geometry/Geography data), and stores it into multiple corresponding data + * structures. * */ protected abstract void parseWkb(); - + /** * Create the WKT representation of Geometry/Geography from the deserialized data. * - * @param sd the Geometry/Geography instance. - * @param isd internal spatial datatype object - * @param pointIndexEnd upper bound for reading points - * @param figureIndexEnd upper bound for reading figures - * @param segmentIndexEnd upper bound for reading segments - * @param shapeIndexEnd upper bound for reading shapes - * @throws SQLServerException + * @param sd + * the Geometry/Geography instance. + * @param isd + * internal spatial datatype object + * @param pointIndexEnd + * upper bound for reading points + * @param figureIndexEnd + * upper bound for reading figures + * @param segmentIndexEnd + * upper bound for reading segments + * @param shapeIndexEnd + * upper bound for reading shapes + * @throws SQLServerException + * if an exception occurs */ - protected void constructWKT(SQLServerSpatialDatatype sd, InternalSpatialDatatype isd, int pointIndexEnd, int figureIndexEnd, - int segmentIndexEnd, int shapeIndexEnd) throws SQLServerException { + protected void constructWKT(SQLServerSpatialDatatype sd, + InternalSpatialDatatype isd, + int pointIndexEnd, + int figureIndexEnd, + int segmentIndexEnd, + int shapeIndexEnd) throws SQLServerException { if (null == points || numberOfPoints == 0) { if (isd.getTypeCode() == 11) { // FULLGLOBE if (sd instanceof Geometry) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalTypeForGeometry")); - throw new SQLServerException(form.format(new Object[]{"Fullglobe"}), null, 0, null); - } else { + throw new SQLServerException(form.format(new Object[] {"Fullglobe"}), null, 0, null); + } + else { appendToWKTBuffers("FULLGLOBE"); return; } @@ -126,15 +141,15 @@ protected void constructWKT(SQLServerSpatialDatatype sd, InternalSpatialDatatype if (isd.getTypeCode() == 7 && currentShapeIndex != shapeIndexEnd - 1) { currentShapeIndex++; appendToWKTBuffers(isd.getTypeName() + "("); - constructWKT(this, InternalSpatialDatatype.valueOf(shapes[currentShapeIndex].getOpenGISType()), - numberOfPoints, numberOfFigures, numberOfSegments, numberOfShapes); + constructWKT(this, InternalSpatialDatatype.valueOf(shapes[currentShapeIndex].getOpenGISType()), numberOfPoints, numberOfFigures, + numberOfSegments, numberOfShapes); appendToWKTBuffers(")"); - return; + return; } appendToWKTBuffers(isd.getTypeName() + " EMPTY"); return; } - + appendToWKTBuffers(isd.getTypeName()); appendToWKTBuffers("("); @@ -167,32 +182,41 @@ protected void constructWKT(SQLServerSpatialDatatype sd, InternalSpatialDatatype break; default: MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } - + appendToWKTBuffers(")"); } - + /** * Parses WKT and populates the data structures of the Geometry/Geography instance. * - * @param sd the Geometry/Geography instance. - * @param startPos The index to start from from the WKT. - * @param parentShapeIndex The index of the parent's Shape in the shapes array. Used to determine this shape's parent. - * @param isGeoCollection flag to indicate if this is part of a GeometryCollection. - * @throws SQLServerException + * @param sd + * the Geometry/Geography instance. + * @param startPos + * The index to start from from the WKT. + * @param parentShapeIndex + * The index of the parent's Shape in the shapes array. Used to determine this shape's parent. + * @param isGeoCollection + * flag to indicate if this is part of a GeometryCollection. + * @throws SQLServerException + * if an exception occurs */ - protected void parseWKTForSerialization(SQLServerSpatialDatatype sd, int startPos, int parentShapeIndex, boolean isGeoCollection) throws SQLServerException { - //after every iteration of this while loop, the currentWktPosition will be set to the - //end of the geometry/geography shape, except for the very first iteration of it. - //This means that there has to be comma (that separates the previous shape with the next shape), - //or we expect a ')' that will close the entire shape and exit the method. - + protected void parseWKTForSerialization(SQLServerSpatialDatatype sd, + int startPos, + int parentShapeIndex, + boolean isGeoCollection) throws SQLServerException { + // after every iteration of this while loop, the currentWktPosition will be set to the + // end of the geometry/geography shape, except for the very first iteration of it. + // This means that there has to be comma (that separates the previous shape with the next shape), + // or we expect a ')' that will close the entire shape and exit the method. + while (hasMoreToken()) { if (startPos != 0) { if (wkt.charAt(currentWktPos) == ')') { return; - } else if (wkt.charAt(currentWktPos) == ',') { + } + else if (wkt.charAt(currentWktPos) == ',') { currentWktPos++; } } @@ -205,51 +229,50 @@ protected void parseWKTForSerialization(SQLServerSpatialDatatype sd, int startPo } catch (Exception e) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } byte fa = 0; - - if (version == 1 && (nextToken.equals("CIRCULARSTRING") || nextToken.equals("COMPOUNDCURVE") || - nextToken.equals("CURVEPOLYGON"))) { + + if (version == 1 && (nextToken.equals("CIRCULARSTRING") || nextToken.equals("COMPOUNDCURVE") || nextToken.equals("CURVEPOLYGON"))) { version = 2; } - + // check for FULLGLOBE before reading the first open bracket, since FULLGLOBE doesn't have one. if (nextToken.equals("FULLGLOBE")) { if (sd instanceof Geometry) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalTypeForGeometry")); - throw new SQLServerException(form.format(new Object[]{"Fullglobe"}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {"Fullglobe"}), null, 0, null); } - + if (startPos != 0) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } - + shapeList.add(new Shape(parentShapeIndex, -1, isd.getTypeCode())); isLargerThanHemisphere = true; version = 2; break; } - // if next keyword is empty, continue the loop. + // if next keyword is empty, continue the loop. if (checkEmptyKeyword(parentShapeIndex, isd, false)) { continue; } - + readOpenBracket(); - + switch (nextToken) { case "POINT": if (startPos == 0 && nextToken.toUpperCase().equals("POINT")) { isSinglePoint = true; } - + if (isGeoCollection) { shapeList.add(new Shape(parentShapeIndex, figureList.size(), isd.getTypeCode())); figureList.add(new Figure(FA_LINE, pointList.size())); } - + readPointWkt(); break; case "LINESTRING": @@ -257,9 +280,9 @@ protected void parseWKTForSerialization(SQLServerSpatialDatatype sd, int startPo shapeList.add(new Shape(parentShapeIndex, figureList.size(), isd.getTypeCode())); fa = isd.getTypeCode() == InternalSpatialDatatype.LINESTRING.getTypeCode() ? FA_STROKE : FA_EXTERIOR_RING; figureList.add(new Figure(fa, pointList.size())); - + readLineWkt(); - + if (startPos == 0 && nextToken.toUpperCase().equals("LINESTRING") && pointList.size() == 2) { isSingleLineSegment = true; } @@ -269,53 +292,53 @@ protected void parseWKTForSerialization(SQLServerSpatialDatatype sd, int startPo case "MULTILINESTRING": thisShapeIndex = shapeList.size(); shapeList.add(new Shape(parentShapeIndex, figureList.size(), isd.getTypeCode())); - + readShapeWkt(thisShapeIndex, nextToken); break; case "MULTIPOLYGON": thisShapeIndex = shapeList.size(); shapeList.add(new Shape(parentShapeIndex, figureList.size(), isd.getTypeCode())); - + readMultiPolygonWkt(thisShapeIndex, nextToken); - + break; case "COMPOUNDCURVE": shapeList.add(new Shape(parentShapeIndex, figureList.size(), isd.getTypeCode())); figureList.add(new Figure(FA_COMPOSITE_CURVE, pointList.size())); - + readCompoundCurveWkt(true); - + break; case "CURVEPOLYGON": shapeList.add(new Shape(parentShapeIndex, figureList.size(), isd.getTypeCode())); readCurvePolygon(); - + break; case "GEOMETRYCOLLECTION": thisShapeIndex = shapeList.size(); shapeList.add(new Shape(parentShapeIndex, figureList.size(), isd.getTypeCode())); - + parseWKTForSerialization(this, currentWktPos, thisShapeIndex, true); - + break; default: MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } readCloseBracket(); } - + populateStructures(); } - + /** - * Constructs and appends a Point type in WKT form to the stringbuffer. - * There are two stringbuffers - WKTsb and WKTsbNoZM. WKTsb contains the X, Y, Z and M coordinates, - * whereas WKTsbNoZM contains only X and Y coordinates. + * Constructs and appends a Point type in WKT form to the stringbuffer. There are two stringbuffers - WKTsb and WKTsbNoZM. WKTsb contains the X, + * Y, Z and M coordinates, whereas WKTsbNoZM contains only X and Y coordinates. * - * @param pointIndex indicates which point to append to the stringbuffer. + * @param pointIndex + * indicates which point to append to the stringbuffer. * */ protected void constructPointWKT(int pointIndex) { @@ -323,80 +346,92 @@ protected void constructPointWKT(int pointIndex) { int secondPointIndex = firstPointIndex + 1; int zValueIndex = pointIndex; int mValueIndex = pointIndex; - + if (points[firstPointIndex] % 1 == 0) { appendToWKTBuffers((int) points[firstPointIndex]); - } else { + } + else { appendToWKTBuffers(points[firstPointIndex]); } appendToWKTBuffers(" "); if (points[secondPointIndex] % 1 == 0) { appendToWKTBuffers((int) points[secondPointIndex]); - } else { + } + else { appendToWKTBuffers(points[secondPointIndex]); } appendToWKTBuffers(" "); - + if (hasZvalues && !Double.isNaN(zValues[zValueIndex]) && !(zValues[zValueIndex] == 0)) { if (zValues[zValueIndex] % 1 == 0) { WKTsb.append((int) zValues[zValueIndex]); - } else { + } + else { WKTsb.append(zValues[zValueIndex]); } WKTsb.append(" "); - + if (hasMvalues && !Double.isNaN(mValues[mValueIndex]) && !(mValues[mValueIndex] <= 0)) { if (mValues[mValueIndex] % 1 == 0) { WKTsb.append((int) mValues[mValueIndex]); - } else { + } + else { WKTsb.append(mValues[mValueIndex]); } WKTsb.append(" "); } } - + currentPointIndex++; // truncate last space WKTsb.setLength(WKTsb.length() - 1); WKTsbNoZM.setLength(WKTsbNoZM.length() - 1); } - /** + /** * Constructs a line in WKT form. * - * @param pointStartIndex . - * @param pointEndIndex . + * @param pointStartIndex + * . + * @param pointEndIndex + * . */ - protected void constructLineWKT(int pointStartIndex, int pointEndIndex) { + protected void constructLineWKT(int pointStartIndex, + int pointEndIndex) { for (int i = pointStartIndex; i < pointEndIndex; i++) { constructPointWKT(i); - + // add ', ' to separate points, except for the last point if (i != pointEndIndex - 1) { appendToWKTBuffers(", "); } } } - + /** * Constructs a shape (simple Geometry/Geography entities that are contained within a single bracket) in WKT form. * - * @param figureStartIndex . - * @param figureEndIndex . + * @param figureStartIndex + * . + * @param figureEndIndex + * . */ - protected void constructShapeWKT(int figureStartIndex, int figureEndIndex) { + protected void constructShapeWKT(int figureStartIndex, + int figureEndIndex) { for (int i = figureStartIndex; i < figureEndIndex; i++) { appendToWKTBuffers("("); - if (i != numberOfFigures - 1) { //not the last figure + if (i != numberOfFigures - 1) { // not the last figure constructLineWKT(figures[i].getPointOffset(), figures[i + 1].getPointOffset()); - } else { + } + else { constructLineWKT(figures[i].getPointOffset(), numberOfPoints); } - + if (i != figureEndIndex - 1) { appendToWKTBuffers("), "); - } else { + } + else { appendToWKTBuffers(")"); } } @@ -405,14 +440,18 @@ protected void constructShapeWKT(int figureStartIndex, int figureEndIndex) { /** * Constructs a mutli-shape (MultiPoint / MultiLineString) in WKT form. * - * @param shapeStartIndex . - * @param shapeEndIndex . + * @param shapeStartIndex + * . + * @param shapeEndIndex + * . */ - protected void constructMultiShapeWKT(int shapeStartIndex, int shapeEndIndex) { + protected void constructMultiShapeWKT(int shapeStartIndex, + int shapeEndIndex) { for (int i = shapeStartIndex + 1; i < shapeEndIndex; i++) { if (shapes[i].getFigureOffset() == -1) { // EMPTY appendToWKTBuffers("EMPTY"); - } else { + } + else { constructShapeWKT(shapes[i].getFigureOffset(), shapes[i].getFigureOffset() + 1); } if (i != shapeEndIndex - 1) { @@ -420,24 +459,29 @@ protected void constructMultiShapeWKT(int shapeStartIndex, int shapeEndIndex) { } } } - + /** * Constructs a CompoundCurve in WKT form. * - * @param segmentStartIndex . - * @param segmentEndIndex . - * @param pointEndIndex . + * @param segmentStartIndex + * . + * @param segmentEndIndex + * . + * @param pointEndIndex + * . */ - protected void constructCompoundcurveWKT(int segmentStartIndex, int segmentEndIndex, int pointEndIndex) { + protected void constructCompoundcurveWKT(int segmentStartIndex, + int segmentEndIndex, + int pointEndIndex) { for (int i = segmentStartIndex; i < segmentEndIndex; i++) { byte segment = segments[i].getSegmentType(); constructSegmentWKT(i, segment, pointEndIndex); - + if (i == segmentEndIndex - 1) { appendToWKTBuffers(")"); break; } - + switch (segment) { case 0: case 2: @@ -456,17 +500,20 @@ protected void constructCompoundcurveWKT(int segmentStartIndex, int segmentEndIn } } } - + /** * Constructs a MultiPolygon in WKT form. * - * @param shapeStartIndex . - * @param shapeEndIndex . + * @param shapeStartIndex + * . + * @param shapeEndIndex + * . */ - protected void constructMultipolygonWKT(int shapeStartIndex, int shapeEndIndex) { + protected void constructMultipolygonWKT(int shapeStartIndex, + int shapeEndIndex) { int figureStartIndex; int figureEndIndex; - + for (int i = shapeStartIndex + 1; i < shapeEndIndex; i++) { figureEndIndex = figures.length; if (shapes[i].getFigureOffset() == -1) { // EMPTY @@ -479,7 +526,8 @@ protected void constructMultipolygonWKT(int shapeStartIndex, int shapeEndIndex) figureStartIndex = shapes[i].getFigureOffset(); if (i == shapes.length - 1) { // last shape figureEndIndex = figures.length; - } else { + } + else { // look ahead and find the next shape that doesn't have -1 as its figure offset (which signifies EMPTY) int tempCurrentShapeIndex = i + 1; // We need to iterate this through until the very end of the shapes list, since if the last shape @@ -488,7 +536,8 @@ protected void constructMultipolygonWKT(int shapeStartIndex, int shapeEndIndex) if (shapes[tempCurrentShapeIndex].getFigureOffset() == -1) { tempCurrentShapeIndex++; continue; - } else { + } + else { figureEndIndex = shapes[tempCurrentShapeIndex].getFigureOffset(); break; } @@ -496,83 +545,96 @@ protected void constructMultipolygonWKT(int shapeStartIndex, int shapeEndIndex) } appendToWKTBuffers("("); - + for (int j = figureStartIndex; j < figureEndIndex; j++) { appendToWKTBuffers("(");// interior ring - + if (j == figures.length - 1) { // last figure constructLineWKT(figures[j].getPointOffset(), numberOfPoints); - } else { + } + else { constructLineWKT(figures[j].getPointOffset(), figures[j + 1].getPointOffset()); } - + if (j == figureEndIndex - 1) { // last polygon of this multipolygon, close off the Multipolygon appendToWKTBuffers(")"); - } else { // not the last polygon, followed by an interior ring + } + else { // not the last polygon, followed by an interior ring appendToWKTBuffers("), "); } } - + appendToWKTBuffers(")"); - + if (!(i == shapeEndIndex - 1)) { // not the last exterior polygon of this multipolygon, add a comma appendToWKTBuffers(", "); } } } - + /** * Constructs a CurvePolygon in WKT form. * - * @param figureStartIndex . - * @param figureEndIndex . - * @param segmentStartIndex . - * @param segmentEndIndex . + * @param figureStartIndex + * . + * @param figureEndIndex + * . + * @param segmentStartIndex + * . + * @param segmentEndIndex + * . */ - protected void constructCurvepolygonWKT(int figureStartIndex, int figureEndIndex, int segmentStartIndex, int segmentEndIndex) { + protected void constructCurvepolygonWKT(int figureStartIndex, + int figureEndIndex, + int segmentStartIndex, + int segmentEndIndex) { for (int i = figureStartIndex; i < figureEndIndex; i++) { switch (figures[i].getFiguresAttribute()) { case 1: // line appendToWKTBuffers("("); - + if (i == figures.length - 1) { constructLineWKT(currentPointIndex, numberOfPoints); - } else { + } + else { constructLineWKT(currentPointIndex, figures[i + 1].getPointOffset()); } - + appendToWKTBuffers(")"); break; case 2: // arc appendToWKTBuffers("CIRCULARSTRING("); - + if (i == figures.length - 1) { constructLineWKT(currentPointIndex, numberOfPoints); - } else { + } + else { constructLineWKT(currentPointIndex, figures[i + 1].getPointOffset()); } - + appendToWKTBuffers(")"); - + break; case 3: // composite curve appendToWKTBuffers("COMPOUNDCURVE("); - + int pointEndIndex = 0; - + if (i == figures.length - 1) { pointEndIndex = numberOfPoints; - } else { + } + else { pointEndIndex = figures[i + 1].getPointOffset(); } - + while (currentPointIndex < pointEndIndex) { byte segment = segments[segmentStartIndex].getSegmentType(); constructSegmentWKT(segmentStartIndex, segment, pointEndIndex); - + if (!(currentPointIndex < pointEndIndex)) { appendToWKTBuffers("))"); - } else { + } + else { switch (segment) { case 0: case 2: @@ -593,53 +655,59 @@ protected void constructCurvepolygonWKT(int figureStartIndex, int figureEndIndex segmentStartIndex++; } - + break; default: return; } - - //Append a comma if this is not the last figure of the shape. + + // Append a comma if this is not the last figure of the shape. if (i != figureEndIndex - 1) { appendToWKTBuffers(", "); } } } - + /** - * Constructs a Segment in WKT form. - * SQL Server re-uses the last point of a segment if the following segment is of type 3 (first arc) or - * type 2 (first line). This makes sense because the last point of a segment and the first point of the next - * segment have to match for a valid curve. This means that the code has to look ahead and decide to decrement - * the currentPointIndex depending on what segment comes next, since it may have been reused (and it's reflected - * in the array of points) + * Constructs a Segment in WKT form. SQL Server re-uses the last point of a segment if the following segment is of type 3 (first arc) or type 2 + * (first line). This makes sense because the last point of a segment and the first point of the next segment have to match for a valid curve. + * This means that the code has to look ahead and decide to decrement the currentPointIndex depending on what segment comes next, since it may + * have been reused (and it's reflected in the array of points) * - * @param currentSegment . - * @param segment . - * @param pointEndIndex . + * @param currentSegment + * . + * @param segment + * . + * @param pointEndIndex + * . */ - protected void constructSegmentWKT(int currentSegment, byte segment, int pointEndIndex) { + protected void constructSegmentWKT(int currentSegment, + byte segment, + int pointEndIndex) { switch (segment) { case 0: appendToWKTBuffers(", "); constructLineWKT(currentPointIndex, currentPointIndex + 1); - + if (currentSegment == segments.length - 1) { // last segment break; - } else if (segments[currentSegment + 1].getSegmentType() != 0) { // not being followed by another line, but not the last segment + } + else if (segments[currentSegment + 1].getSegmentType() != 0) { // not being followed by another line, but not the last segment currentPointIndex = currentPointIndex - 1; incrementPointNumStartIfPointNotReused(pointEndIndex); } break; - + case 1: appendToWKTBuffers(", "); constructLineWKT(currentPointIndex, currentPointIndex + 2); - + if (currentSegment == segments.length - 1) { // last segment break; - } else if (segments[currentSegment + 1].getSegmentType() != 1) { // not being followed by another arc, but not the last segment - currentPointIndex = currentPointIndex - 1; // only increment pointNumStart by one less than what we should be, since the last point will be reused + } + else if (segments[currentSegment + 1].getSegmentType() != 1) { // not being followed by another arc, but not the last segment + currentPointIndex = currentPointIndex - 1; // only increment pointNumStart by one less than what we should be, since the last + // point will be reused incrementPointNumStartIfPointNotReused(pointEndIndex); } @@ -647,23 +715,27 @@ protected void constructSegmentWKT(int currentSegment, byte segment, int pointEn case 2: appendToWKTBuffers("("); constructLineWKT(currentPointIndex, currentPointIndex + 2); - + if (currentSegment == segments.length - 1) { // last segment break; - } else if (segments[currentSegment + 1].getSegmentType() != 0) { // not being followed by another line, but not the last segment - currentPointIndex = currentPointIndex - 1; // only increment pointNumStart by one less than what we should be, since the last point will be reused + } + else if (segments[currentSegment + 1].getSegmentType() != 0) { // not being followed by another line, but not the last segment + currentPointIndex = currentPointIndex - 1; // only increment pointNumStart by one less than what we should be, since the last + // point will be reused incrementPointNumStartIfPointNotReused(pointEndIndex); } - + break; case 3: appendToWKTBuffers("CIRCULARSTRING("); constructLineWKT(currentPointIndex, currentPointIndex + 3); - + if (currentSegment == segments.length - 1) { // last segment break; - } else if (segments[currentSegment + 1].getSegmentType() != 1) { // not being followed by another arc - currentPointIndex = currentPointIndex - 1; // only increment pointNumStart by one less than what we should be, since the last point will be reused + } + else if (segments[currentSegment + 1].getSegmentType() != 1) { // not being followed by another arc + currentPointIndex = currentPointIndex - 1; // only increment pointNumStart by one less than what we should be, since the last + // point will be reused incrementPointNumStartIfPointNotReused(pointEndIndex); } @@ -672,75 +744,76 @@ protected void constructSegmentWKT(int currentSegment, byte segment, int pointEn return; } } - + /** * The starting point for constructing a GeometryCollection type in WKT form. * - * @param shapeEndIndex . - * @throws SQLServerException + * @param shapeEndIndex + * . + * @throws SQLServerException + * if an exception occurs */ protected void constructGeometryCollectionWKT(int shapeEndIndex) throws SQLServerException { currentShapeIndex++; constructGeometryCollectionWKThelper(shapeEndIndex); } - + /** - * Reads Point WKT and adds it to the list of points. - * This method will read up until and including the comma that may come at the end of the Point WKT. - * @throws SQLServerException + * Reads Point WKT and adds it to the list of points. This method will read up until and including the comma that may come at the end of the Point + * WKT. + * + * @throws SQLServerException + * if an exception occurs */ protected void readPointWkt() throws SQLServerException { int numOfCoordinates = 0; double sign; double coords[] = new double[4]; - + while (numOfCoordinates < 4) { sign = 1; if (wkt.charAt(currentWktPos) == '-') { sign = -1; currentWktPos++; } - + int startPos = currentWktPos; - + if (wkt.charAt(currentWktPos) == ')') { break; } - - while (currentWktPos < wkt.length() && - (Character.isDigit(wkt.charAt(currentWktPos)) - || wkt.charAt(currentWktPos) == '.' - || wkt.charAt(currentWktPos) == 'E' - || wkt.charAt(currentWktPos) == 'e')) { + + while (currentWktPos < wkt.length() && (Character.isDigit(wkt.charAt(currentWktPos)) || wkt.charAt(currentWktPos) == '.' + || wkt.charAt(currentWktPos) == 'E' || wkt.charAt(currentWktPos) == 'e')) { currentWktPos++; } - + try { - coords[numOfCoordinates] = sign * - new BigDecimal(wkt.substring(startPos, currentWktPos)).doubleValue(); - } catch (Exception e) { //modify to conversion exception + coords[numOfCoordinates] = sign * new BigDecimal(wkt.substring(startPos, currentWktPos)).doubleValue(); + } + catch (Exception e) { // modify to conversion exception MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } - + numOfCoordinates++; - + skipWhiteSpaces(); - + // After skipping white space after the 4th coordinate has been read, the next // character has to be either a , or ), or the WKT is invalid. if (numOfCoordinates == 4) { if (wkt.charAt(currentWktPos) != ',' && wkt.charAt(currentWktPos) != ')') { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } } - + if (wkt.charAt(currentWktPos) == ',') { // need at least 2 coordinates if (numOfCoordinates == 1) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } currentWktPos++; skipWhiteSpaces(); @@ -748,86 +821,99 @@ protected void readPointWkt() throws SQLServerException { } skipWhiteSpaces(); } - + if (numOfCoordinates == 4) { hasZvalues = true; hasMvalues = true; - } else if (numOfCoordinates == 3) { + } + else if (numOfCoordinates == 3) { hasZvalues = true; } - + pointList.add(new Point(coords[0], coords[1], coords[2], coords[3])); } - + /** * Reads a series of Point types. - * @throws SQLServerException + * + * @throws SQLServerException + * if an exception occurs */ protected void readLineWkt() throws SQLServerException { while (currentWktPos < wkt.length() && wkt.charAt(currentWktPos) != ')') { readPointWkt(); } } - + /** * Reads a shape (simple Geometry/Geography entities that are contained within a single bracket) WKT. * - * @param parentShapeIndex shape index of the parent shape that called this method - * @param nextToken next string token - * @throws SQLServerException + * @param parentShapeIndex + * shape index of the parent shape that called this method + * @param nextToken + * next string token + * @throws SQLServerException + * if an exception occurs */ - protected void readShapeWkt(int parentShapeIndex, String nextToken) throws SQLServerException { + protected void readShapeWkt(int parentShapeIndex, + String nextToken) throws SQLServerException { byte fa = FA_POINT; while (currentWktPos < wkt.length() && wkt.charAt(currentWktPos) != ')') { - + // if next keyword is empty, continue the loop. // Do not check this for polygon. - if (!nextToken.equals("POLYGON") && - checkEmptyKeyword(parentShapeIndex, InternalSpatialDatatype.valueOf(nextToken), true)) { + if (!nextToken.equals("POLYGON") && checkEmptyKeyword(parentShapeIndex, InternalSpatialDatatype.valueOf(nextToken), true)) { continue; } - + if (nextToken.equals("MULTIPOINT")) { shapeList.add(new Shape(parentShapeIndex, figureList.size(), InternalSpatialDatatype.POINT.getTypeCode())); - } else if (nextToken.equals("MULTILINESTRING")) { + } + else if (nextToken.equals("MULTILINESTRING")) { shapeList.add(new Shape(parentShapeIndex, figureList.size(), InternalSpatialDatatype.LINESTRING.getTypeCode())); } - + if (version == 1) { if (nextToken.equals("MULTIPOINT")) { fa = FA_STROKE; - } else if (nextToken.equals("MULTILINESTRING") || nextToken.equals("POLYGON")) { + } + else if (nextToken.equals("MULTILINESTRING") || nextToken.equals("POLYGON")) { fa = FA_EXTERIOR_RING; } version_one_shape_indexes.add(figureList.size()); - } else if (version == 2) { - if (nextToken.equals("MULTIPOINT") || nextToken.equals("MULTILINESTRING") || - nextToken.equals("POLYGON") || nextToken.equals("MULTIPOLYGON")) { + } + else if (version == 2) { + if (nextToken.equals("MULTIPOINT") || nextToken.equals("MULTILINESTRING") || nextToken.equals("POLYGON") + || nextToken.equals("MULTIPOLYGON")) { fa = FA_LINE; } } - + figureList.add(new Figure(fa, pointList.size())); readOpenBracket(); readLineWkt(); readCloseBracket(); skipWhiteSpaces(); - + if (wkt.charAt(currentWktPos) == ',') { // more rings to follow readComma(); - } else if (wkt.charAt(currentWktPos) == ')') { // about to exit while loop + } + else if (wkt.charAt(currentWktPos) == ')') { // about to exit while loop continue; - } else { // unexpected input + } + else { // unexpected input MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } } } - + /** * Reads a CurvePolygon WKT - * @throws SQLServerException + * + * @throws SQLServerException + * if an exception occurs */ protected void readCurvePolygon() throws SQLServerException { while (currentWktPos < wkt.length() && wkt.charAt(currentWktPos) != ')') { @@ -837,28 +923,33 @@ protected void readCurvePolygon() throws SQLServerException { readOpenBracket(); readLineWkt(); readCloseBracket(); - } else if (nextPotentialToken.equals("COMPOUNDCURVE")) { + } + else if (nextPotentialToken.equals("COMPOUNDCURVE")) { figureList.add(new Figure(FA_COMPOSITE_CURVE, pointList.size())); readOpenBracket(); readCompoundCurveWkt(true); readCloseBracket(); - } else if (wkt.charAt(currentWktPos) == '(') { //LineString + } + else if (wkt.charAt(currentWktPos) == '(') { // LineString figureList.add(new Figure(FA_LINE, pointList.size())); readOpenBracket(); readLineWkt(); readCloseBracket(); - } else { + } + else { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } - + if (wkt.charAt(currentWktPos) == ',') { // more polygons to follow readComma(); - } else if (wkt.charAt(currentWktPos) == ')') { // about to exit while loop + } + else if (wkt.charAt(currentWktPos) == ')') { // about to exit while loop continue; - } else { // unexpected input + } + else { // unexpected input MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } } } @@ -866,62 +957,74 @@ protected void readCurvePolygon() throws SQLServerException { /** * Reads a MultiPolygon WKT * - * @param thisShapeIndex shape index of current shape - * @param nextToken next string token - * @throws SQLServerException + * @param thisShapeIndex + * shape index of current shape + * @param nextToken + * next string token + * @throws SQLServerException + * if an exception occurs */ - protected void readMultiPolygonWkt(int thisShapeIndex, String nextToken) throws SQLServerException { + protected void readMultiPolygonWkt(int thisShapeIndex, + String nextToken) throws SQLServerException { while (currentWktPos < wkt.length() && wkt.charAt(currentWktPos) != ')') { if (checkEmptyKeyword(thisShapeIndex, InternalSpatialDatatype.valueOf(nextToken), true)) { continue; } - shapeList.add(new Shape(thisShapeIndex, figureList.size(), InternalSpatialDatatype.POLYGON.getTypeCode())); //exterior polygon + shapeList.add(new Shape(thisShapeIndex, figureList.size(), InternalSpatialDatatype.POLYGON.getTypeCode())); // exterior polygon readOpenBracket(); readShapeWkt(thisShapeIndex, nextToken); readCloseBracket(); - + if (wkt.charAt(currentWktPos) == ',') { // more polygons to follow readComma(); - } else if (wkt.charAt(currentWktPos) == ')') { // about to exit while loop + } + else if (wkt.charAt(currentWktPos) == ')') { // about to exit while loop continue; - } else { // unexpected input + } + else { // unexpected input MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } } } - + /** * Reads a Segment WKT * - * @param segmentType segment type - * @param isFirstIteration flag that indicates if this is the first iteration from the loop outside - * @throws SQLServerException + * @param segmentType + * segment type + * @param isFirstIteration + * flag that indicates if this is the first iteration from the loop outside + * @throws SQLServerException + * if an exception occurs */ - protected void readSegmentWkt(int segmentType, boolean isFirstIteration) throws SQLServerException { + protected void readSegmentWkt(int segmentType, + boolean isFirstIteration) throws SQLServerException { segmentList.add(new Segment((byte) segmentType)); - + int segmentLength = segmentType; - + // under 2 means 0 or 1 (possible values). 0 (line) has 1 point, and 1 (arc) has 2 points, so increment by one - if (segmentLength < 2) { + if (segmentLength < 2) { segmentLength++; } - + for (int i = 0; i < segmentLength; i++) { - //If a segment type of 2 (first line) or 3 (first arc) is not from the very first iteration of the while loop, - //then the first point has to be a duplicate point from the previous segment, so skip the first point. + // If a segment type of 2 (first line) or 3 (first arc) is not from the very first iteration of the while loop, + // then the first point has to be a duplicate point from the previous segment, so skip the first point. if (i == 0 && !isFirstIteration && segmentType >= 2) { skipFirstPointWkt(); - } else { + } + else { readPointWkt(); } } - + if (currentWktPos < wkt.length() && wkt.charAt(currentWktPos) != ')') { if (segmentType == SEGMENT_FIRST_ARC || segmentType == SEGMENT_ARC) { readSegmentWkt(SEGMENT_ARC, false); - } else if (segmentType == SEGMENT_FIRST_LINE | segmentType == SEGMENT_LINE) { + } + else if (segmentType == SEGMENT_FIRST_LINE | segmentType == SEGMENT_LINE) { readSegmentWkt(SEGMENT_LINE, false); } } @@ -930,8 +1033,10 @@ protected void readSegmentWkt(int segmentType, boolean isFirstIteration) throws /** * Reads a CompoundCurve WKT * - * @param isFirstIteration flag that indicates if this is the first iteration from the loop outside - * @throws SQLServerException + * @param isFirstIteration + * flag that indicates if this is the first iteration from the loop outside + * @throws SQLServerException + * if an exception occurs */ protected void readCompoundCurveWkt(boolean isFirstIteration) throws SQLServerException { while (currentWktPos < wkt.length() && wkt.charAt(currentWktPos) != ')') { @@ -940,31 +1045,34 @@ protected void readCompoundCurveWkt(boolean isFirstIteration) throws SQLServerEx readOpenBracket(); readSegmentWkt(SEGMENT_FIRST_ARC, isFirstIteration); readCloseBracket(); - } else if (wkt.charAt(currentWktPos) == '(') {//LineString + } + else if (wkt.charAt(currentWktPos) == '(') {// LineString readOpenBracket(); readSegmentWkt(SEGMENT_FIRST_LINE, isFirstIteration); readCloseBracket(); - } else { + } + else { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } - + isFirstIteration = false; - + if (wkt.charAt(currentWktPos) == ',') { // more polygons to follow readComma(); - } else if (wkt.charAt(currentWktPos) == ')') { // about to exit while loop + } + else if (wkt.charAt(currentWktPos) == ')') { // about to exit while loop continue; - } else { // unexpected input + } + else { // unexpected input MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } - } + } } /** - * Reads the next string token (usually POINT, LINESTRING, etc.). - * Then increments currentWktPos to the end of the string token. + * Reads the next string token (usually POINT, LINESTRING, etc.). Then increments currentWktPos to the end of the string token. * * @return the next string token */ @@ -977,7 +1085,7 @@ protected String getNextStringToken() { int temp = currentWktPos; currentWktPos = endIndex; skipWhiteSpaces(); - + return wkt.substring(temp, endIndex); } @@ -986,20 +1094,20 @@ protected String getNextStringToken() { */ protected void populateStructures() { if (pointList.size() > 0) { - points = new double[pointList.size() * 2]; - + points = new double[pointList.size() * 2]; + for (int i = 0; i < pointList.size(); i++) { points[i * 2] = pointList.get(i).getX(); points[i * 2 + 1] = pointList.get(i).getY(); } - + if (hasZvalues) { zValues = new double[pointList.size()]; for (int i = 0; i < pointList.size(); i++) { zValues[i] = pointList.get(i).getZ(); } } - + if (hasMvalues) { mValues = new double[pointList.size()]; for (int i = 0; i < pointList.size(); i++) { @@ -1007,7 +1115,7 @@ protected void populateStructures() { } } } - + // if version is 2, then we need to check for potential shapes (polygon & multi-shapes) that were // given their figure attributes as if it was version 1, since we don't know what would be the // version of the geometry/geography before we parse the entire WKT. @@ -1016,15 +1124,15 @@ protected void populateStructures() { figureList.get(version_one_shape_indexes.get(i)).setFiguresAttribute((byte) 1); } } - + if (figureList.size() > 0) { figures = new Figure[figureList.size()]; - + for (int i = 0; i < figureList.size(); i++) { figures[i] = figureList.get(i); } } - + // There is an edge case of empty GeometryCollections being inside other GeometryCollections. In this case, // the figure offset of the very first shape (GeometryCollections) has to be -1, but this is not possible to know until // We've parsed through the entire WKT and confirmed that there are 0 points. @@ -1032,48 +1140,50 @@ protected void populateStructures() { if (pointList.size() == 0 && shapeList.size() > 0 && shapeList.get(0).getOpenGISType() == 7) { shapeList.get(0).setFigureOffset(-1); } - + if (shapeList.size() > 0) { shapes = new Shape[shapeList.size()]; - + for (int i = 0; i < shapeList.size(); i++) { shapes[i] = shapeList.get(i); } } - + if (segmentList.size() > 0) { segments = new Segment[segmentList.size()]; - + for (int i = 0; i < segmentList.size(); i++) { segments[i] = segmentList.get(i); } } - + numberOfPoints = pointList.size(); numberOfFigures = figureList.size(); numberOfShapes = shapeList.size(); numberOfSegments = segmentList.size(); } - + protected void readOpenBracket() throws SQLServerException { skipWhiteSpaces(); if (wkt.charAt(currentWktPos) == '(') { currentWktPos++; skipWhiteSpaces(); - } else { + } + else { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } } - + protected void readCloseBracket() throws SQLServerException { skipWhiteSpaces(); if (wkt.charAt(currentWktPos) == ')') { currentWktPos++; skipWhiteSpaces(); - } else { + } + else { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } } @@ -1081,80 +1191,82 @@ protected boolean hasMoreToken() { skipWhiteSpaces(); return currentWktPos < wkt.length(); } - + protected void createSerializationProperties() { serializationProperties = 0; if (hasZvalues) { serializationProperties += hasZvaluesMask; } - + if (hasMvalues) { serializationProperties += hasMvaluesMask; } - + if (isValid) { serializationProperties += isValidMask; } - + if (isSinglePoint) { serializationProperties += isSinglePointMask; } - + if (isSingleLineSegment) { serializationProperties += isSingleLineSegmentMask; } - + if (version == 2) { if (isLargerThanHemisphere) { serializationProperties += isLargerThanHemisphereMask; } } } - + protected int determineWkbCapacity() { int totalSize = 0; - - totalSize+=6; // SRID + version + SerializationPropertiesByte - + + totalSize += 6; // SRID + version + SerializationPropertiesByte + if (isSinglePoint || isSingleLineSegment) { totalSize += 16 * numberOfPoints; - + if (hasZvalues) { totalSize += 8 * numberOfPoints; } - + if (hasMvalues) { totalSize += 8 * numberOfPoints; } - + return totalSize; } - + int pointSize = 16; if (hasZvalues) { pointSize += 8; } - + if (hasMvalues) { pointSize += 8; } - + totalSize += 12; // 4 bytes for 3 ints, each representing the number of points, shapes and figures totalSize += numberOfPoints * pointSize; totalSize += numberOfFigures * 5; totalSize += numberOfShapes * 9; - + if (version == 2) { totalSize += 4; // 4 bytes for 1 int, representing the number of segments totalSize += numberOfSegments; } - + return totalSize; } - + /** * Append the data to both stringbuffers. - * @param o data to append to the stringbuffers. + * + * @param o + * data to append to the stringbuffers. */ protected void appendToWKTBuffers(Object o) { WKTsb.append(o); @@ -1169,35 +1281,37 @@ protected void interpretSerializationPropBytes() { isSingleLineSegment = (serializationProperties & isSingleLineSegmentMask) != 0; isLargerThanHemisphere = (serializationProperties & isLargerThanHemisphereMask) != 0; } - + protected void readNumberOfPoints() { if (isSinglePoint) { numberOfPoints = 1; - } else if (isSingleLineSegment) { + } + else if (isSingleLineSegment) { numberOfPoints = 2; - } else { + } + else { numberOfPoints = buffer.getInt(); } } - + protected void readZvalues() { zValues = new double[numberOfPoints]; for (int i = 0; i < numberOfPoints; i++) { zValues[i] = buffer.getDouble(); } } - + protected void readMvalues() { mValues = new double[numberOfPoints]; for (int i = 0; i < numberOfPoints; i++) { mValues[i] = buffer.getDouble(); } } - + protected void readNumberOfFigures() { numberOfFigures = buffer.getInt(); } - + protected void readFigures() { byte fa; int po; @@ -1208,11 +1322,11 @@ protected void readFigures() { figures[i] = new Figure(fa, po); } } - + protected void readNumberOfShapes() { numberOfShapes = buffer.getInt(); } - + protected void readShapes() { int po; int fo; @@ -1225,11 +1339,11 @@ protected void readShapes() { shapes[i] = new Shape(po, fo, ogt); } } - + protected void readNumberOfSegments() { numberOfSegments = buffer.getInt(); } - + protected void readSegments() { byte st; segments = new Segment[numberOfSegments]; @@ -1242,37 +1356,46 @@ protected void readSegments() { protected void determineInternalType() { if (isSinglePoint) { internalType = InternalSpatialDatatype.POINT; - } else if (isSingleLineSegment) { + } + else if (isSingleLineSegment) { internalType = InternalSpatialDatatype.LINESTRING; - } else { + } + else { internalType = InternalSpatialDatatype.valueOf(shapes[0].getOpenGISType()); } } - - protected boolean checkEmptyKeyword(int parentShapeIndex, InternalSpatialDatatype isd, boolean isInsideAnotherShape) throws SQLServerException { + + protected boolean checkEmptyKeyword(int parentShapeIndex, + InternalSpatialDatatype isd, + boolean isInsideAnotherShape) throws SQLServerException { String potentialEmptyKeyword = getNextStringToken().toUpperCase(Locale.US); if (potentialEmptyKeyword.equals("EMPTY")) { - + byte typeCode = 0; - + if (isInsideAnotherShape) { byte parentTypeCode = isd.getTypeCode(); if (parentTypeCode == 4) { // MultiPoint typeCode = InternalSpatialDatatype.POINT.getTypeCode(); - } else if (parentTypeCode == 5) { // MultiLineString + } + else if (parentTypeCode == 5) { // MultiLineString typeCode = InternalSpatialDatatype.LINESTRING.getTypeCode(); - } else if (parentTypeCode == 6) { // MultiPolygon + } + else if (parentTypeCode == 6) { // MultiPolygon typeCode = InternalSpatialDatatype.POLYGON.getTypeCode(); - } else if (parentTypeCode == 7) { // GeometryCollection + } + else if (parentTypeCode == 7) { // GeometryCollection typeCode = InternalSpatialDatatype.GEOMETRYCOLLECTION.getTypeCode(); - } else { + } + else { String strError = SQLServerException.getErrString("R_illegalWKT"); throw new SQLServerException(strError, null, 0, null); } - } else { + } + else { typeCode = isd.getTypeCode(); } - + shapeList.add(new Shape(parentShapeIndex, -1, typeCode)); skipWhiteSpaces(); if (currentWktPos < wkt.length() && wkt.charAt(currentWktPos) == ',') { @@ -1281,14 +1404,14 @@ protected boolean checkEmptyKeyword(int parentShapeIndex, InternalSpatialDatatyp } return true; } - + if (!potentialEmptyKeyword.equals("")) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } return false; } - + private void incrementPointNumStartIfPointNotReused(int pointEndIndex) { // We need to increment PointNumStart if the last point was actually not re-used in the points array. // 0 for pointNumEnd indicates that this check is not applicable. @@ -1296,18 +1419,20 @@ private void incrementPointNumStartIfPointNotReused(int pointEndIndex) { currentPointIndex++; } } - + /** * Helper used for resurcive iteration for constructing GeometryCollection in WKT form. * - * @param shapeEndIndex . - * @throws SQLServerException + * @param shapeEndIndex + * . + * @throws SQLServerException + * if an exception occurs */ private void constructGeometryCollectionWKThelper(int shapeEndIndex) throws SQLServerException { - //phase 1: assume that there is no multi - stuff and no geometrycollection + // phase 1: assume that there is no multi - stuff and no geometrycollection while (currentShapeIndex < shapeEndIndex) { InternalSpatialDatatype isd = InternalSpatialDatatype.valueOf(shapes[currentShapeIndex].getOpenGISType()); - + int figureIndex = shapes[currentShapeIndex].getFigureOffset(); int pointIndexEnd = numberOfPoints; int figureIndexEnd = numberOfFigures; @@ -1318,7 +1443,7 @@ private void constructGeometryCollectionWKThelper(int shapeEndIndex) throws SQLS int shapeIndexIncrement = 0; int localCurrentSegmentIndex = 0; int localCurrentShapeIndex = 0; - + switch (isd) { case POINT: figureIndexIncrement++; @@ -1335,27 +1460,28 @@ private void constructGeometryCollectionWKThelper(int shapeEndIndex) throws SQLS if (currentShapeIndex < shapes.length - 1) { figureIndexEnd = shapes[currentShapeIndex + 1].getFigureOffset(); } - + figureIndexIncrement = figureIndexEnd - currentFigureIndex; currentShapeIndex++; - + // Needed to keep track of which segment we are at, inside the for loop localCurrentSegmentIndex = currentSegmentIndex; - + if (isd.equals(InternalSpatialDatatype.CURVEPOLYGON)) { // assume Version 2 for (int i = currentFigureIndex; i < figureIndexEnd; i++) { // Only Compoundcurves (with figure attribute 3) can have segments if (figures[i].getFiguresAttribute() == 3) { - + int pointOffsetEnd; if (i == figures.length - 1) { pointOffsetEnd = numberOfPoints; - } else { + } + else { pointOffsetEnd = figures[i + 1].getPointOffset(); } - + int increment = calculateSegmentIncrement(localCurrentSegmentIndex, pointOffsetEnd - figures[i].getPointOffset()); segmentIndexIncrement = segmentIndexIncrement + increment; @@ -1363,42 +1489,41 @@ private void constructGeometryCollectionWKThelper(int shapeEndIndex) throws SQLS } } } - + segmentIndexEnd = localCurrentSegmentIndex; - + break; case MULTIPOINT: case MULTILINESTRING: case MULTIPOLYGON: - //Multipoint and MultiLineString can go on for multiple Shapes, but eventually - //the parentOffset will signal the end of the object, or it's reached the end of the - //shapes array. - //There is also no possibility that a MultiPoint or MultiLineString would branch - //into another parent. - + // Multipoint and MultiLineString can go on for multiple Shapes, but eventually + // the parentOffset will signal the end of the object, or it's reached the end of the + // shapes array. + // There is also no possibility that a MultiPoint or MultiLineString would branch + // into another parent. + int thisShapesParentOffset = shapes[currentShapeIndex].getParentOffset(); - + int tempShapeIndex = currentShapeIndex; - + // Increment shapeStartIndex to account for the shape index that either Multipoint, MultiLineString // or MultiPolygon takes up tempShapeIndex++; - while (tempShapeIndex < shapes.length && - shapes[tempShapeIndex].getParentOffset() != thisShapesParentOffset) { + while (tempShapeIndex < shapes.length && shapes[tempShapeIndex].getParentOffset() != thisShapesParentOffset) { if (!(tempShapeIndex == shapes.length - 1) && // last iteration, don't check for shapes[tempShapeIndex + 1] !(shapes[tempShapeIndex + 1].getFigureOffset() == -1)) { // disregard EMPTY cases figureIndexEnd = shapes[tempShapeIndex + 1].getFigureOffset(); } tempShapeIndex++; } - + figureIndexIncrement = figureIndexEnd - currentFigureIndex; shapeIndexIncrement = tempShapeIndex - currentShapeIndex; shapeIndexEnd = tempShapeIndex; break; case GEOMETRYCOLLECTION: appendToWKTBuffers(isd.getTypeName()); - + // handle Empty GeometryCollection cases if (shapes[currentShapeIndex].getFigureOffset() == -1) { appendToWKTBuffers(" EMPTY"); @@ -1408,28 +1533,29 @@ private void constructGeometryCollectionWKThelper(int shapeEndIndex) throws SQLS } continue; } - + appendToWKTBuffers("("); - + int geometryCollectionParentIndex = shapes[currentShapeIndex].getParentOffset(); - + // Needed to keep track of which shape we are at, inside the for loop localCurrentShapeIndex = currentShapeIndex; - - while (localCurrentShapeIndex < shapes.length - 1 && - shapes[localCurrentShapeIndex + 1].getParentOffset() > geometryCollectionParentIndex) { + + while (localCurrentShapeIndex < shapes.length - 1 + && shapes[localCurrentShapeIndex + 1].getParentOffset() > geometryCollectionParentIndex) { localCurrentShapeIndex++; } // increment localCurrentShapeIndex one more time since it will be used as a shapeEndIndex parameter // for constructGeometryCollectionWKT, and the shapeEndIndex parameter is used non-inclusively localCurrentShapeIndex++; - + currentShapeIndex++; constructGeometryCollectionWKThelper(localCurrentShapeIndex); - + if (currentShapeIndex < shapeEndIndex) { appendToWKTBuffers("), "); - } else { + } + else { appendToWKTBuffers(")"); } @@ -1437,12 +1563,12 @@ private void constructGeometryCollectionWKThelper(int shapeEndIndex) throws SQLS case COMPOUNDCURVE: if (currentFigureIndex == figures.length - 1) { pointIndexEnd = numberOfPoints; - } else { + } + else { pointIndexEnd = figures[currentFigureIndex + 1].getPointOffset(); } - int increment = calculateSegmentIncrement(currentSegmentIndex, pointIndexEnd - - figures[currentFigureIndex].getPointOffset()); + int increment = calculateSegmentIncrement(currentSegmentIndex, pointIndexEnd - figures[currentFigureIndex].getPointOffset()); segmentIndexIncrement = increment; segmentIndexEnd = currentSegmentIndex + increment; @@ -1455,12 +1581,12 @@ private void constructGeometryCollectionWKThelper(int shapeEndIndex) throws SQLS default: break; } - + constructWKT(this, isd, pointIndexEnd, figureIndexEnd, segmentIndexEnd, shapeIndexEnd); currentFigureIndex = currentFigureIndex + figureIndexIncrement; currentSegmentIndex = currentSegmentIndex + segmentIndexIncrement; currentShapeIndex = currentShapeIndex + shapeIndexIncrement; - + if (currentShapeIndex < shapeEndIndex) { appendToWKTBuffers(", "); } @@ -1468,54 +1594,59 @@ private void constructGeometryCollectionWKThelper(int shapeEndIndex) throws SQLS } /** - * Calculates how many segments will be used by this shape. - * Needed to determine when the shape that uses segments (e.g. CompoundCurve) needs to stop reading - * in cases where the CompoundCurve is included as part of GeometryCollection. + * Calculates how many segments will be used by this shape. Needed to determine when the shape that uses segments (e.g. CompoundCurve) needs to + * stop reading in cases where the CompoundCurve is included as part of GeometryCollection. * - * @param segmentStart . - * @param pointDifference number of points that were assigned to this segment to be used. + * @param segmentStart + * . + * @param pointDifference + * number of points that were assigned to this segment to be used. * @return the number of segments that will be used by this shape. */ private int calculateSegmentIncrement(int segmentStart, int pointDifference) { - + int segmentIncrement = 0; - + while (pointDifference > 0) { switch (segments[segmentStart].getSegmentType()) { case 0: pointDifference = pointDifference - 1; - + if (segmentStart == segments.length - 1 || pointDifference < 1) { // last segment break; - } else if (segments[segmentStart + 1].getSegmentType() != 0) { // one point will be reused + } + else if (segments[segmentStart + 1].getSegmentType() != 0) { // one point will be reused pointDifference = pointDifference + 1; } break; case 1: pointDifference = pointDifference - 2; - + if (segmentStart == segments.length - 1 || pointDifference < 1) { // last segment break; - } else if (segments[segmentStart + 1].getSegmentType() != 1) { // one point will be reused + } + else if (segments[segmentStart + 1].getSegmentType() != 1) { // one point will be reused pointDifference = pointDifference + 1; } break; case 2: pointDifference = pointDifference - 2; - + if (segmentStart == segments.length - 1 || pointDifference < 1) { // last segment break; - } else if (segments[segmentStart + 1].getSegmentType() != 0) { // one point will be reused + } + else if (segments[segmentStart + 1].getSegmentType() != 0) { // one point will be reused pointDifference = pointDifference + 1; } break; case 3: pointDifference = pointDifference - 3; - + if (segmentStart == segments.length - 1 || pointDifference < 1) { // last segment break; - } else if (segments[segmentStart + 1].getSegmentType() != 1) { // one point will be reused + } + else if (segments[segmentStart + 1].getSegmentType() != 1) { // one point will be reused pointDifference = pointDifference + 1; } break; @@ -1525,30 +1656,27 @@ private int calculateSegmentIncrement(int segmentStart, segmentStart++; segmentIncrement++; } - + return segmentIncrement; } private void skipFirstPointWkt() { int numOfCoordinates = 0; - + while (numOfCoordinates < 4) { if (wkt.charAt(currentWktPos) == '-') { currentWktPos++; } - + if (wkt.charAt(currentWktPos) == ')') { break; } - - while (currentWktPos < wkt.length() && - (Character.isDigit(wkt.charAt(currentWktPos)) - || wkt.charAt(currentWktPos) == '.' - || wkt.charAt(currentWktPos) == 'E' - || wkt.charAt(currentWktPos) == 'e')) { + + while (currentWktPos < wkt.length() && (Character.isDigit(wkt.charAt(currentWktPos)) || wkt.charAt(currentWktPos) == '.' + || wkt.charAt(currentWktPos) == 'E' || wkt.charAt(currentWktPos) == 'e')) { currentWktPos++; } - + skipWhiteSpaces(); if (wkt.charAt(currentWktPos) == ',') { currentWktPos++; @@ -1557,22 +1685,23 @@ private void skipFirstPointWkt() { break; } skipWhiteSpaces(); - + numOfCoordinates++; } } - + private void readComma() throws SQLServerException { skipWhiteSpaces(); if (wkt.charAt(currentWktPos) == ',') { currentWktPos++; skipWhiteSpaces(); - } else { + } + else { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_illegalWKTposition")); - throw new SQLServerException(form.format(new Object[]{currentWktPos}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {currentWktPos}), null, 0, null); } } - + private void skipWhiteSpaces() { while (currentWktPos < wkt.length() && Character.isWhitespace(wkt.charAt(currentWktPos))) { currentWktPos++; @@ -1587,20 +1716,21 @@ private void skipWhiteSpaces() { class Figure { private byte figuresAttribute; private int pointOffset; - - Figure(byte figuresAttribute, int pointOffset) { + + Figure(byte figuresAttribute, + int pointOffset) { this.figuresAttribute = figuresAttribute; this.pointOffset = pointOffset; } - + public byte getFiguresAttribute() { return figuresAttribute; } - + public int getPointOffset() { return pointOffset; } - + public void setFiguresAttribute(byte fa) { figuresAttribute = fa; } @@ -1614,29 +1744,31 @@ class Shape { private int parentOffset; private int figureOffset; private byte openGISType; - - Shape(int parentOffset, int figureOffset, byte openGISType) { + + Shape(int parentOffset, + int figureOffset, + byte openGISType) { this.parentOffset = parentOffset; this.figureOffset = figureOffset; this.openGISType = openGISType; } - + public int getParentOffset() { return parentOffset; } - + public int getFigureOffset() { return figureOffset; } - + public byte getOpenGISType() { return openGISType; } - + public void setFigureOffset(int fo) { figureOffset = fo; } - + } /** @@ -1645,11 +1777,11 @@ public void setFigureOffset(int fo) { */ class Segment { private byte segmentType; - + Segment(byte segmentType) { this.segmentType = segmentType; } - + public byte getSegmentType() { return segmentType; } @@ -1664,26 +1796,29 @@ class Point { private final double y; private final double z; private final double m; - - Point(double x, double y, double z, double m) { + + Point(double x, + double y, + double z, + double m) { this.x = x; this.y = y; this.z = z; this.m = m; } - + public double getX() { return x; } - + public double getY() { return y; } - + public double getZ() { return z; } - + public double getM() { return m; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index 592bb8deb..cd56a7b84 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -450,6 +450,7 @@ boolean onDone(TDSReader tdsReader) throws SQLServerException { .getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerStatement"); /** The statement's id for logging info */ + @Override public String toString() { return traceID; } @@ -497,7 +498,6 @@ private static int nextStatementID() { stmtPoolable = false; connection = con; bIsClosed = false; - final int nTypes = 5; // Validate result set type ... if (ResultSet.TYPE_FORWARD_ONLY != nType && ResultSet.TYPE_SCROLL_SENSITIVE != nType && ResultSet.TYPE_SCROLL_INSENSITIVE != nType @@ -615,14 +615,7 @@ private void setDefaultQueryTimeout() { final java.util.logging.Logger getStatementLogger() { return stmtlogger; } - - /** - * Standard handler for unsupported data types. - */ - /* L0 */ final void NotImplemented() throws SQLServerException { - SQLServerException.makeFromDriverError(connection, this, SQLServerException.getErrString("R_notSupported"), null, false); - } - + /** * Close the statement. * @@ -644,6 +637,7 @@ void closeInternal() { connection.removeOpenStatement(this); } + @Override public void close() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "close"); @@ -653,6 +647,7 @@ public void close() throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "close"); } + @Override public void closeOnCompletion() throws SQLException { loggerExternal.entering(getClassNameLogging(), "closeOnCompletion"); @@ -664,15 +659,7 @@ public void closeOnCompletion() throws SQLException { loggerExternal.exiting(getClassNameLogging(), "closeOnCompletion"); } - /** - * Execute a result set query - * - * @param sql - * the SQL query - * @exception SQLServerException - * The SQL was invalid. - * @return a JDBC result set. - */ + @Override public java.sql.ResultSet executeQuery(String sql) throws SQLServerException, SQLTimeoutException { loggerExternal.entering(getClassNameLogging(), "executeQuery", sql); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -690,15 +677,7 @@ final SQLServerResultSet executeQueryInternal(String sql) throws SQLServerExcept return resultSet; } - /** - * Execute a JDBC update - * - * @param sql - * the SQL query - * @exception SQLServerException - * The SQL was invalid. - * @return The number of rows updated. - */ + @Override public int executeUpdate(String sql) throws SQLServerException, SQLTimeoutException { loggerExternal.entering(getClassNameLogging(), "executeUpdate", sql); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -716,17 +695,8 @@ public int executeUpdate(String sql) throws SQLServerException, SQLTimeoutExcept return (int) updateCount; } - /** - * Execute a JDBC update - * - * @param sql - * the SQL query - * @exception SQLServerException - * The SQL was invalid. - * @return The number of rows updated. - */ + @Override public long executeLargeUpdate(String sql) throws SQLServerException, SQLTimeoutException { - DriverJDBCVersion.checkSupportsJDBC42(); loggerExternal.entering(getClassNameLogging(), "executeLargeUpdate", sql); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -739,15 +709,7 @@ public long executeLargeUpdate(String sql) throws SQLServerException, SQLTimeout return updateCount; } - /** - * Execute an update or query. - * - * @param sql - * The update or query. - * @exception SQLServerException - * The SQL statement was not valid. - * @return True if a result set was generated. - */ + @Override public boolean execute(String sql) throws SQLServerException, SQLTimeoutException { loggerExternal.entering(getClassNameLogging(), "execute", sql); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -970,7 +932,7 @@ final void resetForReexecute() throws SQLServerException { * The statment SQL. * @return True if the statement is a select. */ - /* L0 */ final boolean isSelect(String sql) throws SQLServerException { + final boolean isSelect(String sql) throws SQLServerException { checkClosed(); // Used to check just the first letter which would cause // "Set" commands to return true... @@ -1014,7 +976,7 @@ final void resetForReexecute() throws SQLServerException { * the param value * @return String */ - /* L0 */ static String replaceParameterWithString(String str, + static String replaceParameterWithString(String str, char marker, String replaceStr) { int index = 0; @@ -1031,7 +993,7 @@ final void resetForReexecute() throws SQLServerException { * the parameter syntax * @return the result */ - /* L0 */ static String replaceMarkerWithNull(String sql) { + static String replaceMarkerWithNull(String sql) { if (!sql.contains("'")) { String retStr = replaceParameterWithString(sql, '?', "null"); return retStr; @@ -1061,7 +1023,7 @@ final void resetForReexecute() throws SQLServerException { } } - /* L0 */ void checkClosed() throws SQLServerException { + void checkClosed() throws SQLServerException { // Check the connection first so that Statement methods // throw a "Connection closed" exception if the reason // that the statement was closed is because the connection @@ -1074,14 +1036,16 @@ final void resetForReexecute() throws SQLServerException { /* ---------------- JDBC API methods ------------------ */ - /* L0 */ public final int getMaxFieldSize() throws SQLServerException { + @Override + public final int getMaxFieldSize() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getMaxFieldSize"); checkClosed(); loggerExternal.exiting(getClassNameLogging(), "getMaxFieldSize", maxFieldSize); return maxFieldSize; } - /* L0 */ public final void setMaxFieldSize(int max) throws SQLServerException { + @Override + public final void setMaxFieldSize(int max) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "setMaxFieldSize", max); checkClosed(); if (max < 0) { @@ -1093,15 +1057,16 @@ final void resetForReexecute() throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "setMaxFieldSize"); } - /* L0 */ public final int getMaxRows() throws SQLServerException { + @Override + public final int getMaxRows() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getMaxRows"); checkClosed(); loggerExternal.exiting(getClassNameLogging(), "getMaxRows", maxRows); return maxRows; } + @Override public final long getLargeMaxRows() throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); loggerExternal.entering(getClassNameLogging(), "getLargeMaxRows"); @@ -1112,7 +1077,8 @@ public final long getLargeMaxRows() throws SQLServerException { return (long) getMaxRows(); } - /* L0 */ public final void setMaxRows(int max) throws SQLServerException { + @Override + public final void setMaxRows(int max) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setMaxRows", max); checkClosed(); @@ -1130,8 +1096,8 @@ public final long getLargeMaxRows() throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "setMaxRows"); } + @Override public final void setLargeMaxRows(long max) throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setLargeMaxRows", max); @@ -1145,7 +1111,8 @@ public final void setLargeMaxRows(long max) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "setLargeMaxRows"); } - /* L0 */ public final void setEscapeProcessing(boolean enable) throws SQLServerException { + @Override + public final void setEscapeProcessing(boolean enable) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setEscapeProcessing", enable); checkClosed(); @@ -1153,14 +1120,16 @@ public final void setLargeMaxRows(long max) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "setEscapeProcessing"); } - /* L0 */ public final int getQueryTimeout() throws SQLServerException { + @Override + public final int getQueryTimeout() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getQueryTimeout"); checkClosed(); loggerExternal.exiting(getClassNameLogging(), "getQueryTimeout", queryTimeout); return queryTimeout; } - /* L0 */ public final void setQueryTimeout(int seconds) throws SQLServerException { + @Override + public final void setQueryTimeout(int seconds) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "setQueryTimeout", seconds); checkClosed(); if (seconds < 0) { @@ -1172,14 +1141,16 @@ public final void setLargeMaxRows(long max) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "setQueryTimeout"); } - /* L0 */ public final int getCancelQueryTimeout() throws SQLServerException { + @Override + public final int getCancelQueryTimeout() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getCancelQueryTimeout"); checkClosed(); loggerExternal.exiting(getClassNameLogging(), "getCancelQueryTimeout", cancelQueryTimeoutSeconds); return cancelQueryTimeoutSeconds; } - /* L0 */ public final void setCancelQueryTimeout(int seconds) throws SQLServerException { + @Override + public final void setCancelQueryTimeout(int seconds) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "setCancelQueryTimeout", seconds); checkClosed(); if (seconds < 0) { @@ -1191,6 +1162,7 @@ public final void setLargeMaxRows(long max) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "setCancelQueryTimeout"); } + @Override public final void cancel() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "cancel"); checkClosed(); @@ -1203,7 +1175,8 @@ public final void cancel() throws SQLServerException { Vector sqlWarnings; // the SQL warnings chain - /* L0 */ public final SQLWarning getWarnings() throws SQLServerException { + @Override + public final SQLWarning getWarnings() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getWarnings"); checkClosed(); if (sqlWarnings == null) @@ -1213,14 +1186,16 @@ public final void cancel() throws SQLServerException { return warn; } - /* L0 */ public final void clearWarnings() throws SQLServerException { + @Override + public final void clearWarnings() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "clearWarnings"); checkClosed(); sqlWarnings = null; loggerExternal.exiting(getClassNameLogging(), "clearWarnings"); } - /* L0 */ public final void setCursorName(String name) throws SQLServerException { + @Override + public final void setCursorName(String name) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "setCursorName", name); checkClosed(); cursorName = name; @@ -1231,6 +1206,7 @@ final String getCursorName() { return cursorName; } + @Override public final java.sql.ResultSet getResultSet() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getResultSet"); checkClosed(); @@ -1238,7 +1214,8 @@ public final java.sql.ResultSet getResultSet() throws SQLServerException { return resultSet; } - /* L0 */ public final int getUpdateCount() throws SQLServerException { + @Override + public final int getUpdateCount() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getUpdateCount"); checkClosed(); @@ -1252,8 +1229,8 @@ public final java.sql.ResultSet getResultSet() throws SQLServerException { return (int) updateCount; } + @Override public final long getLargeUpdateCount() throws SQLServerException { - DriverJDBCVersion.checkSupportsJDBC42(); loggerExternal.entering(getClassNameLogging(), "getUpdateCount"); checkClosed(); @@ -1324,7 +1301,8 @@ final void processResults() throws SQLServerException { * @return true if the next result is a ResultSet object; false if it is an integer (indicating that it is an update count or there are no more * results). */ - /* L0 */ public final boolean getMoreResults() throws SQLServerException { + @Override + public final boolean getMoreResults() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getMoreResults"); checkClosed(); @@ -1614,14 +1592,7 @@ boolean onInfo(TDSReader tdsReader) throws SQLServerException { // Not an error. Is it a result set? else if (nextResult.isResultSet()) { - // Make sure SQLServerResultSet42 is used for 4.2 and above - if (Util.use42Wrapper() || Util.use43Wrapper()) { - resultSet = new SQLServerResultSet42(this); - } - else { - resultSet = new SQLServerResultSet(this); - } - + resultSet = new SQLServerResultSet(this); return true; } @@ -1672,6 +1643,7 @@ boolean consumeExecOutParam(TDSReader tdsReader) throws SQLServerException { // --------------------------JDBC 2.0----------------------------- + @Override public final void setFetchDirection(int nDir) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setFetchDirection", nDir); @@ -1689,6 +1661,7 @@ public final void setFetchDirection(int nDir) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "setFetchDirection"); } + @Override public final int getFetchDirection() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getFetchDirection"); checkClosed(); @@ -1696,7 +1669,8 @@ public final int getFetchDirection() throws SQLServerException { return nFetchDirection; } - /* L0 */ public final void setFetchSize(int rows) throws SQLServerException { + @Override + public final void setFetchSize(int rows) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setFetchSize", rows); checkClosed(); @@ -1707,27 +1681,31 @@ public final int getFetchDirection() throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "setFetchSize"); } - /* L0 */ public final int getFetchSize() throws SQLServerException { + @Override + public final int getFetchSize() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getFetchSize"); checkClosed(); loggerExternal.exiting(getClassNameLogging(), "getFetchSize", nFetchSize); return nFetchSize; } - /* L0 */ public final int getResultSetConcurrency() throws SQLServerException { + @Override + public final int getResultSetConcurrency() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getResultSetConcurrency"); checkClosed(); loggerExternal.exiting(getClassNameLogging(), "getResultSetConcurrency", resultSetConcurrency); return resultSetConcurrency; } - /* L0 */ public final int getResultSetType() throws SQLServerException { + @Override + public final int getResultSetType() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getResultSetType"); checkClosed(); loggerExternal.exiting(getClassNameLogging(), "getResultSetType", appResultSetType); return appResultSetType; } + @Override public void addBatch(String sql) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "addBatch", sql); checkClosed(); @@ -1741,6 +1719,7 @@ public void addBatch(String sql) throws SQLServerException { loggerExternal.exiting(getClassNameLogging(), "addBatch"); } + @Override public void clearBatch() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "clearBatch"); checkClosed(); @@ -1751,6 +1730,7 @@ public void clearBatch() throws SQLServerException { /** * Send a batch of statements to the database. */ + @Override public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQLTimeoutException { loggerExternal.entering(getClassNameLogging(), "executeBatch"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -1828,8 +1808,8 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL } } // executeBatch + @Override public long[] executeLargeBatch() throws SQLServerException, BatchUpdateException, SQLTimeoutException { - DriverJDBCVersion.checkSupportsJDBC42(); loggerExternal.entering(getClassNameLogging(), "executeLargeBatch"); if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { @@ -1914,7 +1894,8 @@ public long[] executeLargeBatch() throws SQLServerException, BatchUpdateExceptio * when an error occurs * @return the connection */ - /* L0 */ public final java.sql.Connection getConnection() throws SQLServerException { + @Override + public final java.sql.Connection getConnection() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getConnection"); if (bIsClosed) { SQLServerException.makeFromDriverError(connection, this, SQLServerException.getErrString("R_statementIsClosed"), null, false); @@ -2015,7 +1996,8 @@ private void doExecuteCursored(StmtExecCmd execCmd, /* JDBC 3.0 */ - /* L3 */ public final int getResultSetHoldability() throws SQLException { + @Override + public final int getResultSetHoldability() throws SQLException { loggerExternal.entering(getClassNameLogging(), "getResultSetHoldability"); checkClosed(); int holdability = connection.getHoldability(); // For SQL Server must be the same as the connection @@ -2023,6 +2005,7 @@ private void doExecuteCursored(StmtExecCmd execCmd, return holdability; } + @Override public final boolean execute(java.lang.String sql, int autoGeneratedKeys) throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) { @@ -2043,6 +2026,7 @@ public final boolean execute(java.lang.String sql, return null != resultSet; } + @Override public final boolean execute(java.lang.String sql, int[] columnIndexes) throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2056,6 +2040,7 @@ public final boolean execute(java.lang.String sql, return fSuccess; } + @Override public final boolean execute(java.lang.String sql, java.lang.String[] columnNames) throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2069,6 +2054,7 @@ public final boolean execute(java.lang.String sql, return fSuccess; } + @Override public final int executeUpdate(String sql, int autoGeneratedKeys) throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) { @@ -2094,9 +2080,9 @@ public final int executeUpdate(String sql, return (int) updateCount; } + @Override public final long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLServerException, SQLTimeoutException { - DriverJDBCVersion.checkSupportsJDBC42(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) { loggerExternal.entering(getClassNameLogging(), "executeLargeUpdate", new Object[] {sql, autoGeneratedKeys}); @@ -2115,6 +2101,7 @@ public final long executeLargeUpdate(String sql, return updateCount; } + @Override public final int executeUpdate(java.lang.String sql, int[] columnIndexes) throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2128,9 +2115,9 @@ public final int executeUpdate(java.lang.String sql, return count; } + @Override public final long executeLargeUpdate(java.lang.String sql, int[] columnIndexes) throws SQLServerException, SQLTimeoutException { - DriverJDBCVersion.checkSupportsJDBC42(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "executeLargeUpdate", new Object[] {sql, columnIndexes}); @@ -2143,6 +2130,7 @@ public final long executeLargeUpdate(java.lang.String sql, return count; } + @Override public final int executeUpdate(java.lang.String sql, String[] columnNames) throws SQLServerException, SQLTimeoutException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) @@ -2156,9 +2144,9 @@ public final int executeUpdate(java.lang.String sql, return count; } + @Override public final long executeLargeUpdate(java.lang.String sql, String[] columnNames) throws SQLServerException, SQLTimeoutException { - DriverJDBCVersion.checkSupportsJDBC42(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "executeLargeUpdate", new Object[] {sql, columnNames}); @@ -2171,6 +2159,7 @@ public final long executeLargeUpdate(java.lang.String sql, return count; } + @Override public final ResultSet getGeneratedKeys() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getGeneratedKeys"); checkClosed(); @@ -2192,12 +2181,14 @@ public final ResultSet getGeneratedKeys() throws SQLServerException { return autoGeneratedKeys; } - /* L3 */ public final boolean getMoreResults(int mode) throws SQLServerException { + @Override + public final boolean getMoreResults(int mode) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getMoreResults", mode); checkClosed(); - if (KEEP_CURRENT_RESULT == mode) - NotImplemented(); + if (KEEP_CURRENT_RESULT == mode) { + SQLServerException.throwNotSupportedException(connection, this); + } if (CLOSE_CURRENT_RESULT != mode && CLOSE_ALL_RESULTS != mode) SQLServerException.makeFromDriverError(connection, this, SQLServerException.getErrString("R_modeSuppliedNotValid"), null, true); @@ -2217,6 +2208,7 @@ public final ResultSet getGeneratedKeys() throws SQLServerException { return fResults; } + @Override public boolean isClosed() throws SQLException { loggerExternal.entering(getClassNameLogging(), "isClosed"); boolean result = bIsClosed || connection.isSessionUnAvailable(); @@ -2224,6 +2216,7 @@ public boolean isClosed() throws SQLException { return result; } + @Override public boolean isCloseOnCompletion() throws SQLException { loggerExternal.entering(getClassNameLogging(), "isCloseOnCompletion"); checkClosed(); @@ -2231,6 +2224,7 @@ public boolean isCloseOnCompletion() throws SQLException { return isCloseOnCompletion; } + @Override public boolean isPoolable() throws SQLException { loggerExternal.entering(getClassNameLogging(), "isPoolable"); checkClosed(); @@ -2238,6 +2232,7 @@ public boolean isPoolable() throws SQLException { return stmtPoolable; } + @Override public void setPoolable(boolean poolable) throws SQLException { loggerExternal.entering(getClassNameLogging(), "setPoolable", poolable); checkClosed(); @@ -2245,6 +2240,7 @@ public void setPoolable(boolean poolable) throws SQLException { loggerExternal.exiting(getClassNameLogging(), "setPoolable"); } + @Override public boolean isWrapperFor(Class iface) throws SQLException { loggerExternal.entering(getClassNameLogging(), "isWrapperFor"); boolean f = iface.isInstance(this); @@ -2252,6 +2248,7 @@ public boolean isWrapperFor(Class iface) throws SQLException { return f; } + @Override public T unwrap(Class iface) throws SQLException { loggerExternal.entering(getClassNameLogging(), "unwrap"); T t; @@ -2283,6 +2280,8 @@ public T unwrap(Class iface) throws SQLException { // Enables handling very large responses, values // Disadvantages // Reduced concurrency on the server + + @Override public final void setResponseBuffering(String value) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "setResponseBuffering", value); checkClosed(); @@ -2302,6 +2301,7 @@ else if ("adaptive".equalsIgnoreCase(value)) { loggerExternal.exiting(getClassNameLogging(), "setResponseBuffering"); } + @Override public final String getResponseBuffering() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getResponseBuffering"); checkClosed(); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAConnection.java index 424fe52ce..ad7a73a4a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAConnection.java @@ -29,7 +29,7 @@ public final class SQLServerXAConnection extends SQLServerPooledConnection imple private SQLServerConnection physicalControlConnection; private Logger xaLogger; - /* L0 */ SQLServerXAConnection(SQLServerDataSource ds, + SQLServerXAConnection(SQLServerDataSource ds, String user, String pwd) throws java.sql.SQLException { super(ds, user, pwd); @@ -60,7 +60,8 @@ public final class SQLServerXAConnection extends SQLServerPooledConnection imple xaLogger.finer(ds.toString() + " user:" + user); } - /* L0 */ public synchronized XAResource getXAResource() throws java.sql.SQLException { + @Override + public synchronized XAResource getXAResource() throws java.sql.SQLException { // All connections handed out from this physical connection have a common XAResource // for transaction control. IE the XAResource is one to one with the physical connection. @@ -72,6 +73,7 @@ public final class SQLServerXAConnection extends SQLServerPooledConnection imple /** * Closes the physical connection that this PooledConnection object represents. */ + @Override public void close() throws SQLException { synchronized (this) { if (XAResource != null) { @@ -85,5 +87,4 @@ public void close() throws SQLException { } super.close(); } - } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXADataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXADataSource.java index 286ff216d..baaa331ef 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXADataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXADataSource.java @@ -52,7 +52,8 @@ public final class SQLServerXADataSource extends SQLServerConnectionPoolDataSour * @exception SQLException * The database connection failed. */ - /* L0 */ public XAConnection getXAConnection(String user, + @Override + public XAConnection getXAConnection(String user, String password) throws SQLException { if (loggerExternal.isLoggable(Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getXAConnection", new Object[] {user, "Password not traced"}); @@ -84,7 +85,8 @@ public final class SQLServerXADataSource extends SQLServerConnectionPoolDataSour * @exception SQLException * The database connection failed. */ - /* L0 */ public XAConnection getXAConnection() throws SQLException { + @Override + public XAConnection getXAConnection() throws SQLException { if (loggerExternal.isLoggable(Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getXAConnection"); return getXAConnection(getUser(), getPassword()); @@ -92,6 +94,7 @@ public final class SQLServerXADataSource extends SQLServerConnectionPoolDataSour // Implement javax.naming.Referenceable interface methods. + @Override public Reference getReference() { if (loggerExternal.isLoggable(Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getReference"); @@ -129,5 +132,4 @@ private Object readResolve() { return ds; } } - } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAResource.java index be00a276a..7ba25de95 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAResource.java @@ -51,7 +51,7 @@ final class XidImpl implements Xid { * @param bqual * branch id */ - /* L0 */ public XidImpl(int formatId, + public XidImpl(int formatId, byte gtrid[], byte bqual[]) { this.formatId = formatId; @@ -60,15 +60,15 @@ final class XidImpl implements Xid { traceID = " XID:" + xidDisplay(this); } - /* L0 */ public byte[] getGlobalTransactionId() { + public byte[] getGlobalTransactionId() { return gtrid; } - /* L0 */ public byte[] getBranchQualifier() { + public byte[] getBranchQualifier() { return bqual; } - /* L0 */ public int getFormatId() { + public int getFormatId() { return formatId; } @@ -172,11 +172,12 @@ public final class SQLServerXAResource implements javax.transaction.xa.XAResourc xaInitLock = new Object(); } + @Override public String toString() { return traceID; } - /* L0 */ SQLServerXAResource(SQLServerConnection original, + SQLServerXAResource(SQLServerConnection original, SQLServerConnection control, String loginfo) { traceID = " XAResourceID:" + nextResourceID(); @@ -358,7 +359,7 @@ private String typeDisplay(int type) { } - /* L0 */ private XAReturnValue DTC_XA_Interface(int nType, + private XAReturnValue DTC_XA_Interface(int nType, Xid xid, int xaFlags) throws XAException { @@ -746,7 +747,8 @@ else if (-1 != version.indexOf('.')) { return returnStatus; } - /* L0 */ public void start(Xid xid, + @Override + public void start(Xid xid, int flags) throws XAException { /* * Transaction mgr will use this resource in the global transaction. After this call the app server will call getConnection() to get a @@ -771,7 +773,8 @@ else if (-1 != version.indexOf('.')) { DTC_XA_Interface(XA_START, xid, flags); } - /* L0 */ public void end(Xid xid, + @Override + public void end(Xid xid, int flags) throws XAException { // Called by the transaction mgr after the app closes the connection it was given from this physical // connection @@ -785,7 +788,8 @@ else if (-1 != version.indexOf('.')) { DTC_XA_Interface(XA_END, xid, flags | tightlyCoupled); } - /* L0 */ public int prepare(Xid xid) throws XAException { + @Override + public int prepare(Xid xid) throws XAException { /* * Ask the resource manager to prepare for a transaction commit of the transaction specified in xid. Parameters: xid - A global transaction * identifier Returns: A value indicating the resource manager's vote on the outcome of the transaction. The possible values are: XA_RDONLY or @@ -799,20 +803,24 @@ else if (-1 != version.indexOf('.')) { return nStatus; } - /* L0 */ public void commit(Xid xid, + @Override + public void commit(Xid xid, boolean onePhase) throws XAException { DTC_XA_Interface(XA_COMMIT, xid, ((onePhase) ? TMONEPHASE : TMNOFLAGS) | tightlyCoupled); } - /* L0 */ public void rollback(Xid xid) throws XAException { + @Override + public void rollback(Xid xid) throws XAException { DTC_XA_Interface(XA_ROLLBACK, xid, tightlyCoupled); } - /* L0 */ public void forget(Xid xid) throws XAException { + @Override + public void forget(Xid xid) throws XAException { DTC_XA_Interface(XA_FORGET, xid, tightlyCoupled); } - /* L0 */ public Xid[] recover(int flags) throws XAException { + @Override + public Xid[] recover(int flags) throws XAException { XAReturnValue r = DTC_XA_Interface(XA_RECOVER, null, flags | tightlyCoupled); int offset = 0; ArrayList al = new ArrayList<>(); @@ -859,7 +867,8 @@ else if (-1 != version.indexOf('.')) { return xids; } - /* L0 */ public boolean isSameRM(XAResource xares) throws XAException { + @Override + public boolean isSameRM(XAResource xares) throws XAException { // A Resource Manager (RM) is an instance of a connection to a DB if (xaLogger.isLoggable(Level.FINER)) @@ -872,7 +881,8 @@ else if (-1 != version.indexOf('.')) { return jxa.sResourceManagerId.equals(this.sResourceManagerId); } - /* L0 */ public boolean setTransactionTimeout(int seconds) throws XAException { + @Override + public boolean setTransactionTimeout(int seconds) throws XAException { isTransacrionTimeoutSet = 1; timeoutSeconds = seconds; @@ -881,7 +891,8 @@ else if (-1 != version.indexOf('.')) { return true; } - /* L0 */ public int getTransactionTimeout() throws XAException { + @Override + public int getTransactionTimeout() throws XAException { return timeoutSeconds; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java index c8842ae09..d2858efb1 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java @@ -984,28 +984,6 @@ static synchronized boolean checkIfNeedNewAccessToken(SQLServerConnection connec return false; } - - static final boolean use42Wrapper; - - static { - boolean supportJDBC42 = true; - try { - DriverJDBCVersion.checkSupportsJDBC42(); - } - catch (UnsupportedOperationException e) { - supportJDBC42 = false; - } - - double jvmVersion = Double.parseDouble(Util.SYSTEM_SPEC_VERSION); - - use42Wrapper = supportJDBC42 && (1.8 <= jvmVersion); - } - - // if driver is for JDBC 42 and jvm version is 8 or higher, then always return as SQLServerPreparedStatement42, - // otherwise return SQLServerPreparedStatement - static boolean use42Wrapper() { - return use42Wrapper; - } static final boolean use43Wrapper; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dataclassification/InformationType.java b/src/main/java/com/microsoft/sqlserver/jdbc/dataclassification/InformationType.java index d38aabac0..8e0b4ea03 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dataclassification/InformationType.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dataclassification/InformationType.java @@ -8,8 +8,6 @@ package com.microsoft.sqlserver.jdbc.dataclassification; -import java.beans.ConstructorProperties; - /** * This class retrieves Information Types as recieved from SQL Server for the * active resultSet @@ -26,7 +24,6 @@ public class InformationType { * @param id * ID of Information Type */ - @ConstructorProperties({"name", "id"}) public InformationType(String name, String id) { this.name = name; this.id = id; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dataclassification/Label.java b/src/main/java/com/microsoft/sqlserver/jdbc/dataclassification/Label.java index 1ac08d0f8..7ccbeddec 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dataclassification/Label.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dataclassification/Label.java @@ -8,8 +8,6 @@ package com.microsoft.sqlserver.jdbc.dataclassification; -import java.beans.ConstructorProperties; - /** * This class retrieves Labels as recieved from SQL Server for the active * resultSet @@ -26,7 +24,6 @@ public class Label { * @param id * ID of Label */ - @ConstructorProperties({"name", "id"}) public Label(String name, String id) { this.name = name; this.id = id; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java b/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java index fda54026a..b19b45c96 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java @@ -157,13 +157,13 @@ public void setShardingKeyIfValidTest() throws TestAbortedException, SQLExceptio connection43.setShardingKeyIfValid(shardingKey, 10); } catch (SQLException e) { - assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); + assert (e.getMessage().contains(TestResource.getResource("R_operationNotSupported"))); } try { connection43.setShardingKeyIfValid(shardingKey, superShardingKey, 10); } catch (SQLException e) { - assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); + assert (e.getMessage().contains(TestResource.getResource("R_operationNotSupported"))); } } @@ -182,13 +182,13 @@ public void setShardingKeyTest() throws TestAbortedException, SQLException { connection43.setShardingKey(shardingKey); } catch (SQLException e) { - assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); + assert (e.getMessage().contains(TestResource.getResource("R_operationNotSupported"))); } try { connection43.setShardingKey(shardingKey, superShardingKey); } catch (SQLException e) { - assert (e.getMessage().contains(TestResource.getResource("R_notImplemented"))); + assert (e.getMessage().contains(TestResource.getResource("R_operationNotSupported"))); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyISQLServerBulkRecordTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyISQLServerBulkRecordTest.java index 1be4ad850..83316cc22 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyISQLServerBulkRecordTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyISQLServerBulkRecordTest.java @@ -1,204 +1,206 @@ -/* - * 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.bulkCopy; - -import java.sql.JDBCType; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ThreadLocalRandom; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.platform.runner.JUnitPlatform; -import org.junit.runner.RunWith; - -import com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord; -import com.microsoft.sqlserver.jdbc.SQLServerException; -import com.microsoft.sqlserver.testframework.AbstractTest; -import com.microsoft.sqlserver.testframework.DBConnection; -import com.microsoft.sqlserver.testframework.DBStatement; -import com.microsoft.sqlserver.testframework.DBTable; -import com.microsoft.sqlserver.testframework.sqlType.SqlType; - -/** - * Test bulkcopy decimal sacle and precision - */ -@RunWith(JUnitPlatform.class) -@DisplayName("Test ISQLServerBulkRecord") -public class BulkCopyISQLServerBulkRecordTest extends AbstractTest { - - @Test - void testISQLServerBulkRecord() throws SQLException { - try (DBConnection con = new DBConnection(connectionString); - DBStatement stmt = con.createStatement()) { - DBTable dstTable = new DBTable(true); - stmt.createTable(dstTable); - BulkData Bdata = new BulkData(dstTable); - - BulkCopyTestWrapper bulkWrapper = new BulkCopyTestWrapper(connectionString); - bulkWrapper.setUsingConnection((0 == ThreadLocalRandom.current().nextInt(2)) ? true : false); - BulkCopyTestUtil.performBulkCopy(bulkWrapper, Bdata, dstTable); - } - } - - class BulkData implements ISQLServerBulkRecord { - - private class ColumnMetadata { - String columnName; - int columnType; - int precision; - int scale; - - ColumnMetadata(String name, - int type, - int precision, - int scale) { - columnName = name; - columnType = type; - this.precision = precision; - this.scale = scale; - } - } - - int totalColumn = 0; - int counter = 0; - int rowCount = 1; - Map columnMetadata; - List data; - - BulkData(DBTable dstTable) { - columnMetadata = new HashMap<>(); - totalColumn = dstTable.totalColumns(); - - // add metadata - for (int i = 0; i < totalColumn; i++) { - SqlType sqlType = dstTable.getSqlType(i); - int precision = sqlType.getPrecision(); - if (JDBCType.TIMESTAMP == sqlType.getJdbctype()) { - // TODO: update the test to use correct precision once bulkCopy is fixed - precision = 50; - } - columnMetadata.put(i + 1, - new ColumnMetadata(sqlType.getName(), sqlType.getJdbctype().getVendorTypeNumber(), precision, sqlType.getScale())); - } - - // add data - rowCount = dstTable.getTotalRows(); - data = new ArrayList<>(rowCount); - for (int i = 0; i < rowCount; i++) { - Object[] CurrentRow = new Object[totalColumn]; - for (int j = 0; j < totalColumn; j++) { - SqlType sqlType = dstTable.getSqlType(j); - if (JDBCType.BIT == sqlType.getJdbctype()) { - CurrentRow[j] = ((0 == ThreadLocalRandom.current().nextInt(2)) ? Boolean.FALSE : Boolean.TRUE); - } - else - { - CurrentRow[j] = sqlType.createdata(); - } - } - data.add(CurrentRow); - } - } - - /* - * (non-Javadoc) - * - * @see com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord#getColumnOrdinals() - */ - @Override - public Set getColumnOrdinals() { - return columnMetadata.keySet(); - } - - /* - * (non-Javadoc) - * - * @see com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord#getColumnName(int) - */ - @Override - public String getColumnName(int column) { - return columnMetadata.get(column).columnName; - } - - /* - * (non-Javadoc) - * - * @see com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord#getColumnType(int) - */ - @Override - public int getColumnType(int column) { - return columnMetadata.get(column).columnType; - } - - /* - * (non-Javadoc) - * - * @see com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord#getPrecision(int) - */ - @Override - public int getPrecision(int column) { - return columnMetadata.get(column).precision; - } - - /* - * (non-Javadoc) - * - * @see com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord#getScale(int) - */ - @Override - public int getScale(int column) { - return columnMetadata.get(column).scale; - } - - /* - * (non-Javadoc) - * - * @see com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord#isAutoIncrement(int) - */ - @Override - public boolean isAutoIncrement(int column) { - return false; - } - - /* - * (non-Javadoc) - * - * @see com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord#getRowData() - */ - @Override - public Object[] getRowData() throws SQLServerException { - return data.get(counter++); - } - - /* - * (non-Javadoc) - * - * @see com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord#next() - */ - @Override - public boolean next() throws SQLServerException { - if (counter < rowCount) - return true; - return false; - } - - /** - * reset the counter when using the interface for validating the data - */ - public void reset() { - counter = 0; - } - } -} +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ +package com.microsoft.sqlserver.jdbc.bulkCopy; + +import java.sql.JDBCType; +import java.sql.SQLException; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord; +import com.microsoft.sqlserver.jdbc.SQLServerException; +import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.DBConnection; +import com.microsoft.sqlserver.testframework.DBStatement; +import com.microsoft.sqlserver.testframework.DBTable; +import com.microsoft.sqlserver.testframework.sqlType.SqlType; + +/** + * Test bulkcopy decimal sacle and precision + */ +@RunWith(JUnitPlatform.class) +@DisplayName("Test ISQLServerBulkRecord") +public class BulkCopyISQLServerBulkRecordTest extends AbstractTest { + + @Test + void testISQLServerBulkRecord() throws SQLException { + try (DBConnection con = new DBConnection(connectionString); DBStatement stmt = con.createStatement()) { + DBTable dstTable = new DBTable(true); + stmt.createTable(dstTable); + BulkData Bdata = new BulkData(dstTable); + + BulkCopyTestWrapper bulkWrapper = new BulkCopyTestWrapper(connectionString); + bulkWrapper.setUsingConnection((0 == ThreadLocalRandom.current().nextInt(2)) ? true : false); + BulkCopyTestUtil.performBulkCopy(bulkWrapper, Bdata, dstTable); + } + } + + class BulkData implements ISQLServerBulkRecord { + + private class ColumnMetadata { + String columnName; + int columnType; + int precision; + int scale; + + ColumnMetadata(String name, + int type, + int precision, + int scale) { + columnName = name; + columnType = type; + this.precision = precision; + this.scale = scale; + } + } + + int totalColumn = 0; + int counter = 0; + int rowCount = 1; + Map columnMetadata; + List data; + + BulkData(DBTable dstTable) { + columnMetadata = new HashMap<>(); + totalColumn = dstTable.totalColumns(); + + // add metadata + for (int i = 0; i < totalColumn; i++) { + SqlType sqlType = dstTable.getSqlType(i); + int precision = sqlType.getPrecision(); + if (JDBCType.TIMESTAMP == sqlType.getJdbctype()) { + // TODO: update the test to use correct precision once bulkCopy is fixed + precision = 50; + } + columnMetadata.put(i + 1, + new ColumnMetadata(sqlType.getName(), sqlType.getJdbctype().getVendorTypeNumber(), precision, sqlType.getScale())); + } + + // add data + rowCount = dstTable.getTotalRows(); + data = new ArrayList<>(rowCount); + for (int i = 0; i < rowCount; i++) { + Object[] CurrentRow = new Object[totalColumn]; + for (int j = 0; j < totalColumn; j++) { + SqlType sqlType = dstTable.getSqlType(j); + if (JDBCType.BIT == sqlType.getJdbctype()) { + CurrentRow[j] = ((0 == ThreadLocalRandom.current().nextInt(2)) ? Boolean.FALSE : Boolean.TRUE); + } + else { + CurrentRow[j] = sqlType.createdata(); + } + } + data.add(CurrentRow); + } + } + + @Override + public Set getColumnOrdinals() { + return columnMetadata.keySet(); + } + + @Override + public String getColumnName(int column) { + return columnMetadata.get(column).columnName; + } + + @Override + public int getColumnType(int column) { + return columnMetadata.get(column).columnType; + } + + @Override + public int getPrecision(int column) { + return columnMetadata.get(column).precision; + } + + @Override + public int getScale(int column) { + return columnMetadata.get(column).scale; + } + + @Override + public boolean isAutoIncrement(int column) { + return false; + } + + @Override + public Object[] getRowData() throws SQLServerException { + return data.get(counter++); + } + + @Override + public boolean next() throws SQLServerException { + if (counter < rowCount) + return true; + return false; + } + + /** + * reset the counter when using the interface for validating the data + */ + public void reset() { + counter = 0; + } + + @Override + public void addColumnMetadata(int positionInFile, + String name, + int jdbcType, + int precision, + int scale, + DateTimeFormatter dateTimeFormatter) throws SQLServerException { + // TODO Not Implemented + } + + @Override + public void addColumnMetadata(int positionInFile, + String name, + int jdbcType, + int precision, + int scale) throws SQLServerException { + // TODO Not Implemented + } + + @Override + public void setTimestampWithTimezoneFormat(String dateTimeFormat) { + // TODO Not Implemented + } + + @Override + public void setTimestampWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { + // TODO Not Implemented + } + + @Override + public void setTimeWithTimezoneFormat(String timeFormat) { + // TODO Not Implemented + } + + @Override + public void setTimeWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { + // TODO Not Implemented + } + + @Override + public DateTimeFormatter getColumnDateTimeFormatter(int column) { + // TODO Not Implemented + return null; + } + } +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/ISQLServerBulkRecordIssuesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/ISQLServerBulkRecordIssuesTest.java index f030fc72d..bb24c65dc 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/ISQLServerBulkRecordIssuesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/ISQLServerBulkRecordIssuesTest.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Set; import java.text.MessageFormat; +import java.time.format.DateTimeFormatter; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; @@ -42,407 +43,427 @@ @RunWith(JUnitPlatform.class) public class ISQLServerBulkRecordIssuesTest extends AbstractTest { - static Statement stmt = null; - static PreparedStatement pStmt = null; - static String query; - static SQLServerConnection con = null; - static String srcTable = "sourceTable"; - static String destTable = "destTable"; - String variation; - - /** - * Testing that sending a bigger varchar(3) to varchar(2) is thowing the proper error message. - * - * @throws Exception - */ - @Test - public void testVarchar() throws Exception { - variation = "testVarchar"; - BulkData bData = new BulkData(variation); - query = "CREATE TABLE " + destTable + " (smallDATA varchar(2))"; - stmt.executeUpdate(query); - - try (SQLServerBulkCopy bcOperation = new SQLServerBulkCopy(connectionString)) { - bcOperation.setDestinationTableName(destTable); - bcOperation.writeToServer(bData); - bcOperation.close(); - fail(TestResource.getResource("R_expectedFailPassed")); - } - catch (Exception e) { - if (e instanceof SQLException) { - assertTrue(e.getMessage().contains(TestResource.getResource("R_givenValueType")), TestResource.getResource("R_invalidErrorMessage") + e.toString()); - } - else { - fail(e.getMessage()); - } - } - } - - /** - * Testing that setting scale and precision 0 in column meta data for smalldatetime should work - * - * @throws Exception - */ - @Test - public void testSmalldatetime() throws Exception { - variation = "testSmalldatetime"; - BulkData bData = new BulkData(variation); - String value = ("1954-05-22 02:44:00.0").toString(); - query = "CREATE TABLE " + destTable + " (smallDATA smalldatetime)"; - stmt.executeUpdate(query); - - try (SQLServerBulkCopy bcOperation = new SQLServerBulkCopy(connectionString)) { - bcOperation.setDestinationTableName(destTable); - bcOperation.writeToServer(bData); - - try (ResultSet rs = stmt.executeQuery("select * from " + destTable)) { - while (rs.next()) { - assertEquals(rs.getString(1), value); - } - } - } - } - - /** - * Testing that setting out of range value for small datetime is throwing the proper message - * - * @throws Exception - */ - @Test - public void testSmalldatetimeOutofRange() throws Exception { - variation = "testSmalldatetimeOutofRange"; - BulkData bData = new BulkData(variation); - - query = "CREATE TABLE " + destTable + " (smallDATA smalldatetime)"; - stmt.executeUpdate(query); - - try (SQLServerBulkCopy bcOperation = new SQLServerBulkCopy(connectionString)) { - bcOperation.setDestinationTableName(destTable); - bcOperation.writeToServer(bData); - fail("BulkCopy executed for testSmalldatetimeOutofRange when it it was expected to fail"); - } - catch (Exception e) { - if (e instanceof SQLException) { - MessageFormat form = new MessageFormat(TestResource.getResource("R_conversionFailed")); - Object[] msgArgs = {"character string", "smalldatetime"}; - - assertTrue(e.getMessage().contains(form.format(msgArgs)), - TestResource.getResource("R_invalidErrorMessage") + e.toString()); - } - else { - fail(e.getMessage()); - } - } - } - - /** - * Test binary out of length (sending length of 6 to binary (5)) - * - * @throws Exception - */ - @Test - public void testBinaryColumnAsByte() throws Exception { - variation = "testBinaryColumnAsByte"; - BulkData bData = new BulkData(variation); - query = "CREATE TABLE " + destTable + " (col1 binary(5))"; - stmt.executeUpdate(query); - - try (SQLServerBulkCopy bcOperation = new SQLServerBulkCopy(connectionString)) { - bcOperation.setDestinationTableName(destTable); - bcOperation.writeToServer(bData); - fail(TestResource.getResource("R_expectedFailPassed")); - } - catch (Exception e) { - if (e instanceof SQLException) { - assertTrue(e.getMessage().contains(TestResource.getResource("R_givenValueType")), TestResource.getResource("R_invalidErrorMessage") + e.toString()); - } - else { - fail(e.getMessage()); - } - } - } - - /** - * Test sending longer value for binary column while data is sent as string format - * - * @throws Exception - */ - @Test - public void testBinaryColumnAsString() throws Exception { - variation = "testBinaryColumnAsString"; - BulkData bData = new BulkData(variation); - query = "CREATE TABLE " + destTable + " (col1 binary(5))"; - stmt.executeUpdate(query); - - try (SQLServerBulkCopy bcOperation = new SQLServerBulkCopy(connectionString)) { - bcOperation.setDestinationTableName(destTable); - bcOperation.writeToServer(bData); - fail(TestResource.getResource("R_expectedFailPassed")); - } - catch (Exception e) { - if (e instanceof SQLException) { - assertTrue(e.getMessage().contains(TestResource.getResource("R_givenValueType")), TestResource.getResource("R_invalidErrorMessage") + e.toString()); - } - else { - fail(e.getMessage()); - } - } - } - - /** - * Verify that sending valid value in string format for binary column is successful - * - * @throws Exception - */ - @Test - public void testSendValidValueforBinaryColumnAsString() throws Exception { - variation = "testSendValidValueforBinaryColumnAsString"; - BulkData bData = new BulkData(variation); - query = "CREATE TABLE " + destTable + " (col1 binary(5))"; - stmt.executeUpdate(query); - - try (SQLServerBulkCopy bcOperation = new SQLServerBulkCopy(connectionString)) { - bcOperation.setDestinationTableName(destTable); - bcOperation.writeToServer(bData); - - try (ResultSet rs = stmt.executeQuery("select * from " + destTable)) { - while (rs.next()) { - assertEquals(rs.getString(1), "0101010000"); - } - } - } - catch (Exception e) { - fail(e.getMessage()); - } - } - - /** - * Prepare test - * - * @throws SQLException - * @throws SecurityException - * @throws IOException - */ - @BeforeAll - public static void setupHere() throws SQLException, SecurityException, IOException { - con = (SQLServerConnection) DriverManager.getConnection(connectionString); - stmt = con.createStatement(); - Utils.dropTableIfExists(destTable, stmt); - Utils.dropTableIfExists(srcTable, stmt); - } - - /** - * Clean up - * - * @throws SQLException - */ - @AfterEach - public void afterEachTests() throws SQLException { - Utils.dropTableIfExists(destTable, stmt); - Utils.dropTableIfExists(srcTable, stmt); - } - - @AfterAll - public static void afterAllTests() throws SQLException { - if (null != stmt) { - stmt.close(); - } - if (null != con) { - con.close(); - } - } + static Statement stmt = null; + static PreparedStatement pStmt = null; + static String query; + static SQLServerConnection con = null; + static String srcTable = "sourceTable"; + static String destTable = "destTable"; + String variation; + + /** + * Testing that sending a bigger varchar(3) to varchar(2) is thowing the + * proper error message. + * + * @throws Exception + */ + @Test + public void testVarchar() throws Exception { + variation = "testVarchar"; + BulkData bData = new BulkData(variation); + query = "CREATE TABLE " + destTable + " (smallDATA varchar(2))"; + stmt.executeUpdate(query); + + try (SQLServerBulkCopy bcOperation = new SQLServerBulkCopy( + connectionString)) { + bcOperation.setDestinationTableName(destTable); + bcOperation.writeToServer(bData); + bcOperation.close(); + fail(TestResource.getResource("R_expectedFailPassed")); + } catch (Exception e) { + if (e instanceof SQLException) { + assertTrue( + e.getMessage().contains( + TestResource.getResource("R_givenValueType")), + TestResource.getResource("R_invalidErrorMessage") + + e.toString()); + } else { + fail(e.getMessage()); + } + } + } + + /** + * Testing that setting scale and precision 0 in column meta data for + * smalldatetime should work + * + * @throws Exception + */ + @Test + public void testSmalldatetime() throws Exception { + variation = "testSmalldatetime"; + BulkData bData = new BulkData(variation); + String value = ("1954-05-22 02:44:00.0").toString(); + query = "CREATE TABLE " + destTable + " (smallDATA smalldatetime)"; + stmt.executeUpdate(query); + + try (SQLServerBulkCopy bcOperation = new SQLServerBulkCopy( + connectionString)) { + bcOperation.setDestinationTableName(destTable); + bcOperation.writeToServer(bData); + + try (ResultSet rs = stmt + .executeQuery("select * from " + destTable)) { + while (rs.next()) { + assertEquals(rs.getString(1), value); + } + } + } + } + + /** + * Testing that setting out of range value for small datetime is throwing + * the proper message + * + * @throws Exception + */ + @Test + public void testSmalldatetimeOutofRange() throws Exception { + variation = "testSmalldatetimeOutofRange"; + BulkData bData = new BulkData(variation); + + query = "CREATE TABLE " + destTable + " (smallDATA smalldatetime)"; + stmt.executeUpdate(query); + + try (SQLServerBulkCopy bcOperation = new SQLServerBulkCopy( + connectionString)) { + bcOperation.setDestinationTableName(destTable); + bcOperation.writeToServer(bData); + fail("BulkCopy executed for testSmalldatetimeOutofRange when it it was expected to fail"); + } catch (Exception e) { + if (e instanceof SQLException) { + MessageFormat form = new MessageFormat( + TestResource.getResource("R_conversionFailed")); + Object[] msgArgs = {"character string", "smalldatetime"}; + + assertTrue(e.getMessage().contains(form.format(msgArgs)), + TestResource.getResource("R_invalidErrorMessage") + + e.toString()); + } else { + fail(e.getMessage()); + } + } + } + + /** + * Test binary out of length (sending length of 6 to binary (5)) + * + * @throws Exception + */ + @Test + public void testBinaryColumnAsByte() throws Exception { + variation = "testBinaryColumnAsByte"; + BulkData bData = new BulkData(variation); + query = "CREATE TABLE " + destTable + " (col1 binary(5))"; + stmt.executeUpdate(query); + + try (SQLServerBulkCopy bcOperation = new SQLServerBulkCopy( + connectionString)) { + bcOperation.setDestinationTableName(destTable); + bcOperation.writeToServer(bData); + fail(TestResource.getResource("R_expectedFailPassed")); + } catch (Exception e) { + if (e instanceof SQLException) { + assertTrue( + e.getMessage().contains( + TestResource.getResource("R_givenValueType")), + TestResource.getResource("R_invalidErrorMessage") + + e.toString()); + } else { + fail(e.getMessage()); + } + } + } + + /** + * Test sending longer value for binary column while data is sent as string + * format + * + * @throws Exception + */ + @Test + public void testBinaryColumnAsString() throws Exception { + variation = "testBinaryColumnAsString"; + BulkData bData = new BulkData(variation); + query = "CREATE TABLE " + destTable + " (col1 binary(5))"; + stmt.executeUpdate(query); + + try (SQLServerBulkCopy bcOperation = new SQLServerBulkCopy( + connectionString)) { + bcOperation.setDestinationTableName(destTable); + bcOperation.writeToServer(bData); + fail(TestResource.getResource("R_expectedFailPassed")); + } catch (Exception e) { + if (e instanceof SQLException) { + assertTrue( + e.getMessage().contains( + TestResource.getResource("R_givenValueType")), + TestResource.getResource("R_invalidErrorMessage") + + e.toString()); + } else { + fail(e.getMessage()); + } + } + } + + /** + * Verify that sending valid value in string format for binary column is + * successful + * + * @throws Exception + */ + @Test + public void testSendValidValueforBinaryColumnAsString() throws Exception { + variation = "testSendValidValueforBinaryColumnAsString"; + BulkData bData = new BulkData(variation); + query = "CREATE TABLE " + destTable + " (col1 binary(5))"; + stmt.executeUpdate(query); + + try (SQLServerBulkCopy bcOperation = new SQLServerBulkCopy( + connectionString)) { + bcOperation.setDestinationTableName(destTable); + bcOperation.writeToServer(bData); + + try (ResultSet rs = stmt + .executeQuery("select * from " + destTable)) { + while (rs.next()) { + assertEquals(rs.getString(1), "0101010000"); + } + } + } catch (Exception e) { + fail(e.getMessage()); + } + } + + /** + * Prepare test + * + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ + @BeforeAll + public static void setupHere() + throws SQLException, SecurityException, IOException { + con = (SQLServerConnection) DriverManager + .getConnection(connectionString); + stmt = con.createStatement(); + Utils.dropTableIfExists(destTable, stmt); + Utils.dropTableIfExists(srcTable, stmt); + } + + /** + * Clean up + * + * @throws SQLException + */ + @AfterEach + public void afterEachTests() throws SQLException { + Utils.dropTableIfExists(destTable, stmt); + Utils.dropTableIfExists(srcTable, stmt); + } + + @AfterAll + public static void afterAllTests() throws SQLException { + if (null != stmt) { + stmt.close(); + } + if (null != con) { + con.close(); + } + } } class BulkData implements ISQLServerBulkRecord { - boolean isStringData = false; - - private class ColumnMetadata { - String columnName; - int columnType; - int precision; - int scale; - - ColumnMetadata(String name, - int type, - int precision, - int scale) { - columnName = name; - columnType = type; - this.precision = precision; - this.scale = scale; - } - } - - Map columnMetadata; - ArrayList dateData; - ArrayList stringData; - ArrayList byteData; - - int counter = 0; - int rowCount = 1; - - BulkData(String variation) { - if (variation.equalsIgnoreCase("testVarchar")) { - isStringData = true; - columnMetadata = new HashMap<>(); - - columnMetadata.put(1, new ColumnMetadata("varchar(2)", java.sql.Types.VARCHAR, 0, 0)); - - stringData = new ArrayList<>(); - stringData.add(new String("aaa")); - rowCount = stringData.size(); - } - else if (variation.equalsIgnoreCase("testSmalldatetime")) { - isStringData = false; - columnMetadata = new HashMap<>(); - - columnMetadata.put(1, new ColumnMetadata("smallDatetime", java.sql.Types.TIMESTAMP, 0, 0)); - - dateData = new ArrayList<>(); - dateData.add(Timestamp.valueOf("1954-05-22 02:43:37.123")); - rowCount = dateData.size(); - } - else if (variation.equalsIgnoreCase("testSmalldatetimeOutofRange")) { - isStringData = false; - columnMetadata = new HashMap<>(); - - columnMetadata.put(1, new ColumnMetadata("smallDatetime", java.sql.Types.TIMESTAMP, 0, 0)); - - dateData = new ArrayList<>(); - dateData.add(Timestamp.valueOf("1954-05-22 02:43:37.1234")); - rowCount = dateData.size(); - - } - else if (variation.equalsIgnoreCase("testBinaryColumnAsByte")) { - isStringData = false; - columnMetadata = new HashMap<>(); - - columnMetadata.put(1, new ColumnMetadata("binary(5)", java.sql.Types.BINARY, 5, 0)); - - byteData = new ArrayList<>(); - byteData.add("helloo".getBytes()); - rowCount = byteData.size(); - - } - else if (variation.equalsIgnoreCase("testBinaryColumnAsString")) { - isStringData = true; - columnMetadata = new HashMap<>(); - - columnMetadata.put(1, new ColumnMetadata("binary(5)", java.sql.Types.BINARY, 5, 0)); - - stringData = new ArrayList<>(); - stringData.add("616368697412"); - rowCount = stringData.size(); - - } - - else if (variation.equalsIgnoreCase("testSendValidValueforBinaryColumnAsString")) { - isStringData = true; - columnMetadata = new HashMap<>(); - - columnMetadata.put(1, new ColumnMetadata("binary(5)", java.sql.Types.BINARY, 5, 0)); - - stringData = new ArrayList<>(); - stringData.add("010101"); - rowCount = stringData.size(); - - } - counter = 0; - - } - - /* - * (non-Javadoc) - * - * @see com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord#getColumnOrdinals() - */ - @Override - public Set getColumnOrdinals() { - return columnMetadata.keySet(); - } - - /* - * (non-Javadoc) - * - * @see com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord#getColumnName(int) - */ - @Override - public String getColumnName(int column) { - return columnMetadata.get(column).columnName; - } - - /* - * (non-Javadoc) - * - * @see com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord#getColumnType(int) - */ - @Override - public int getColumnType(int column) { - return columnMetadata.get(column).columnType; - } - - /* - * (non-Javadoc) - * - * @see com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord#getPrecision(int) - */ - @Override - public int getPrecision(int column) { - return columnMetadata.get(column).precision; - } - - /* - * (non-Javadoc) - * - * @see com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord#getScale(int) - */ - @Override - public int getScale(int column) { - return columnMetadata.get(column).scale; - } - - /* - * (non-Javadoc) - * - * @see com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord#isAutoIncrement(int) - */ - @Override - public boolean isAutoIncrement(int column) { - return false; - } - - /* - * (non-Javadoc) - * - * @see com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord#getRowData() - */ - @Override - public Object[] getRowData() throws SQLServerException { - Object[] dataRow = new Object[columnMetadata.size()]; - if (isStringData) - dataRow[0] = stringData.get(counter); - else { - if (null != dateData) - dataRow[0] = dateData.get(counter); - else if (null != byteData) - dataRow[0] = byteData.get(counter); - } - counter++; - return dataRow; - } - - /* - * (non-Javadoc) - * - * @see com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord#next() - */ - @Override - public boolean next() throws SQLServerException { - if (counter < rowCount) { - return true; - } - return false; - } + boolean isStringData = false; + + private class ColumnMetadata { + String columnName; + int columnType; + int precision; + int scale; + + ColumnMetadata(String name, int type, int precision, int scale) { + columnName = name; + columnType = type; + this.precision = precision; + this.scale = scale; + } + } + + Map columnMetadata; + ArrayList dateData; + ArrayList stringData; + ArrayList byteData; + + int counter = 0; + int rowCount = 1; + + BulkData(String variation) { + if (variation.equalsIgnoreCase("testVarchar")) { + isStringData = true; + columnMetadata = new HashMap<>(); + + columnMetadata.put(1, new ColumnMetadata("varchar(2)", + java.sql.Types.VARCHAR, 0, 0)); + + stringData = new ArrayList<>(); + stringData.add(new String("aaa")); + rowCount = stringData.size(); + } else if (variation.equalsIgnoreCase("testSmalldatetime")) { + isStringData = false; + columnMetadata = new HashMap<>(); + + columnMetadata.put(1, new ColumnMetadata("smallDatetime", + java.sql.Types.TIMESTAMP, 0, 0)); + + dateData = new ArrayList<>(); + dateData.add(Timestamp.valueOf("1954-05-22 02:43:37.123")); + rowCount = dateData.size(); + } else if (variation.equalsIgnoreCase("testSmalldatetimeOutofRange")) { + isStringData = false; + columnMetadata = new HashMap<>(); + + columnMetadata.put(1, new ColumnMetadata("smallDatetime", + java.sql.Types.TIMESTAMP, 0, 0)); + + dateData = new ArrayList<>(); + dateData.add(Timestamp.valueOf("1954-05-22 02:43:37.1234")); + rowCount = dateData.size(); + + } else if (variation.equalsIgnoreCase("testBinaryColumnAsByte")) { + isStringData = false; + columnMetadata = new HashMap<>(); + + columnMetadata.put(1, new ColumnMetadata("binary(5)", + java.sql.Types.BINARY, 5, 0)); + + byteData = new ArrayList<>(); + byteData.add("helloo".getBytes()); + rowCount = byteData.size(); + + } else if (variation.equalsIgnoreCase("testBinaryColumnAsString")) { + isStringData = true; + columnMetadata = new HashMap<>(); + + columnMetadata.put(1, new ColumnMetadata("binary(5)", + java.sql.Types.BINARY, 5, 0)); + + stringData = new ArrayList<>(); + stringData.add("616368697412"); + rowCount = stringData.size(); + + } + + else if (variation.equalsIgnoreCase( + "testSendValidValueforBinaryColumnAsString")) { + isStringData = true; + columnMetadata = new HashMap<>(); + + columnMetadata.put(1, new ColumnMetadata("binary(5)", + java.sql.Types.BINARY, 5, 0)); + + stringData = new ArrayList<>(); + stringData.add("010101"); + rowCount = stringData.size(); + + } + counter = 0; + + } + + @Override + public Set getColumnOrdinals() { + return columnMetadata.keySet(); + } + + @Override + public String getColumnName(int column) { + return columnMetadata.get(column).columnName; + } + + @Override + public int getColumnType(int column) { + return columnMetadata.get(column).columnType; + } + + @Override + public int getPrecision(int column) { + return columnMetadata.get(column).precision; + } + + @Override + public int getScale(int column) { + return columnMetadata.get(column).scale; + } + + @Override + public boolean isAutoIncrement(int column) { + return false; + } + + @Override + public Object[] getRowData() throws SQLServerException { + Object[] dataRow = new Object[columnMetadata.size()]; + if (isStringData) + dataRow[0] = stringData.get(counter); + else { + if (null != dateData) + dataRow[0] = dateData.get(counter); + else if (null != byteData) + dataRow[0] = byteData.get(counter); + } + counter++; + return dataRow; + } + + @Override + public boolean next() throws SQLServerException { + if (counter < rowCount) { + return true; + } + return false; + } + + @Override + public void addColumnMetadata(int positionInFile, String name, int jdbcType, + int precision, int scale, DateTimeFormatter dateTimeFormatter) + throws SQLServerException { + // TODO Not Implemented + } + + @Override + public void addColumnMetadata(int positionInFile, String name, int jdbcType, + int precision, int scale) throws SQLServerException { + // TODO Not Implemented + } + + @Override + public void setTimestampWithTimezoneFormat(String dateTimeFormat) { + // TODO Not Implemented + } + + @Override + public void setTimestampWithTimezoneFormat( + DateTimeFormatter dateTimeFormatter) { + // TODO Not Implemented + } + + @Override + public void setTimeWithTimezoneFormat(String timeFormat) { + // TODO Not Implemented + } + + @Override + public void setTimeWithTimezoneFormat(DateTimeFormatter dateTimeFormatter) { + // TODO Not Implemented + } + + @Override + public DateTimeFormatter getColumnDateTimeFormatter(int column) { + // TODO Not Implemented + return null; + } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java index c0ec92b58..d98dc4ebb 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java @@ -62,11 +62,11 @@ public void testComments() throws Exception { String valid = "/* rando comment *//* rando comment */ INSERT /* rando comment */ INTO /* rando comment *//*rando comment*/ PeterTable /*rando comment */" + " /* rando comment */values/* rando comment */ (1, 2)"; - Field f1 = pstmt.getClass().getSuperclass().getDeclaredField("localUserSQL"); + Field f1 = pstmt.getClass().getDeclaredField("localUserSQL"); f1.setAccessible(true); f1.set(pstmt, valid); - Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class, boolean.class, boolean.class); + Method method = pstmt.getClass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class, boolean.class, boolean.class); method.setAccessible(true); assertEquals((String) method.invoke(pstmt, false, false, false, false), "PeterTable"); @@ -80,11 +80,11 @@ public void testBrackets() throws Exception { String valid = "/* rando comment *//* rando comment */ INSERT /* rando comment */ INTO /* rando comment *//*rando comment*/ [Peter[]]Table] /*rando comment */" + " /* rando comment */values/* rando comment */ (1, 2)"; - Field f1 = pstmt.getClass().getSuperclass().getDeclaredField("localUserSQL"); + Field f1 = pstmt.getClass().getDeclaredField("localUserSQL"); f1.setAccessible(true); f1.set(pstmt, valid); - Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class, boolean.class, boolean.class); + Method method = pstmt.getClass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class, boolean.class, boolean.class); method.setAccessible(true); assertEquals((String) method.invoke(pstmt, false, false, false, false), "[Peter[]]Table]"); @@ -98,17 +98,18 @@ public void testDoubleQuotes() throws Exception { String valid = "/* rando comment *//* rando comment */ INSERT /* rando comment */ INTO /* rando comment *//*rando comment*/ \"Peter\"\"\"\"Table\" /*rando comment */" + " /* rando comment */values/* rando comment */ (1, 2)"; - Field f1 = pstmt.getClass().getSuperclass().getDeclaredField("localUserSQL"); + Field f1 = pstmt.getClass().getDeclaredField("localUserSQL"); f1.setAccessible(true); f1.set(pstmt, valid); - Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class, boolean.class, boolean.class); + Method method = pstmt.getClass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class, boolean.class, boolean.class); method.setAccessible(true); assertEquals((String) method.invoke(pstmt, false, false, false, false), "\"Peter\"\"\"\"Table\""); } } + @SuppressWarnings("unchecked") @Test public void testAll() throws Exception { try (Connection connection = DriverManager.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); @@ -117,16 +118,16 @@ public void testAll() throws Exception { + " /* rando comment */ (\"c1\"/* rando comment */, /* rando comment */[c2]/* rando comment */, /* rando comment */ /* rando comment */c3/* rando comment */, c4)" + "values/* rando comment */ (/* rando comment */1/* rando comment */, /* rando comment */2/* rando comment */ , '?', ?)/* rando comment */"; - Field f1 = pstmt.getClass().getSuperclass().getDeclaredField("localUserSQL"); + Field f1 = pstmt.getClass().getDeclaredField("localUserSQL"); f1.setAccessible(true); f1.set(pstmt, valid); - Method method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class, boolean.class, boolean.class); + Method method = pstmt.getClass().getDeclaredMethod("parseUserSQLForTableNameDW", boolean.class, boolean.class, boolean.class, boolean.class); method.setAccessible(true); assertEquals((String) method.invoke(pstmt, false, false, false, false), "\"Peter\"\"\"\"Table\""); - method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForColumnListDW"); + method = pstmt.getClass().getDeclaredMethod("parseUserSQLForColumnListDW"); method.setAccessible(true); ArrayList columnList = (ArrayList) method.invoke(pstmt); @@ -140,7 +141,7 @@ public void testAll() throws Exception { assertEquals(columnList.get(i), columnListExpected.get(i)); } - method = pstmt.getClass().getSuperclass().getDeclaredMethod("parseUserSQLForValueListDW", boolean.class); + method = pstmt.getClass().getDeclaredMethod("parseUserSQLForValueListDW", boolean.class); method.setAccessible(true); ArrayList valueList = (ArrayList) method.invoke(pstmt, false); @@ -342,10 +343,6 @@ public void testAllFilledColumns() throws Exception { f1.setAccessible(true); f1.set(connection, true); - Timestamp myTimestamp = new Timestamp(114550L); - - Date d = new Date(114550L); - pstmt.addBatch(); pstmt.executeBatch(); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetWrapper42Test.java b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetWrapper42Test.java deleted file mode 100644 index 78612674e..000000000 --- a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetWrapper42Test.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ -package com.microsoft.sqlserver.jdbc.resultset; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.platform.runner.JUnitPlatform; -import org.junit.runner.RunWith; - -import com.microsoft.sqlserver.jdbc.SQLServerResultSet; -import com.microsoft.sqlserver.jdbc.SQLServerResultSet42; -import com.microsoft.sqlserver.testframework.AbstractTest; - -/** - * Test SQLServerResultSet42 class - * - */ -@RunWith(JUnitPlatform.class) -public class ResultSetWrapper42Test extends AbstractTest { - static Connection connection = null; - double javaVersion = Double.parseDouble(System.getProperty("java.specification.version")); - static int major; - static int minor; - - /** - * Tests creation of SQLServerResultSet42 object - * - * @throws SQLException - */ - @Test - public void SQLServerResultSet42Test() throws SQLException { - String sql = "SELECT SUSER_SNAME()"; - - ResultSet rs = null; - try { - rs = connection.createStatement().executeQuery(sql); - - if (1.8d <= javaVersion && 4 == major && 2 == minor) { - assertTrue(rs instanceof SQLServerResultSet42); - } - else { - assertTrue(rs instanceof SQLServerResultSet); - } - } - finally { - if (null != rs) { - rs.close(); - } - } - } - - @BeforeAll - private static void setupConnection() throws SQLException { - connection = DriverManager.getConnection(connectionString); - - DatabaseMetaData metadata = connection.getMetaData(); - major = metadata.getJDBCMajorVersion(); - minor = metadata.getJDBCMinorVersion(); - } - - @AfterAll - private static void terminateVariation() throws SQLException { - if (null != connection) { - connection.close(); - } - } - -} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/Wrapper42Test.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/Wrapper42Test.java deleted file mode 100644 index 8322deb60..000000000 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/Wrapper42Test.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ -package com.microsoft.sqlserver.jdbc.unit.statement; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.sql.CallableStatement; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.SQLException; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.platform.runner.JUnitPlatform; -import org.junit.runner.RunWith; - -import com.microsoft.sqlserver.jdbc.SQLServerCallableStatement; -import com.microsoft.sqlserver.jdbc.SQLServerCallableStatement42; -import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; -import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement42; -import com.microsoft.sqlserver.testframework.AbstractTest; - -/** - * Test SQLServerPreparedSatement42 and SQLServerCallableSatement42 classes - * - */ -@RunWith(JUnitPlatform.class) -public class Wrapper42Test extends AbstractTest { - static Connection connection = null; - double javaVersion = Double.parseDouble(System.getProperty("java.specification.version")); - static int major; - static int minor; - - /** - * Tests creation of SQLServerPreparedSatement42 object - * - * @throws SQLException - */ - @Test - public void PreparedSatement42Test() throws SQLException { - String sql = "SELECT SUSER_SNAME()"; - - PreparedStatement pstmt = connection.prepareStatement(sql); - - if (1.8d <= javaVersion && 4 == major && 2 == minor) { - assertTrue(pstmt instanceof SQLServerPreparedStatement42); - } - else { - assertTrue(pstmt instanceof SQLServerPreparedStatement); - } - } - - /** - * Tests creation of SQLServerCallableStatement42 object - * - * @throws SQLException - */ - @Test - public void CallableStatement42Test() throws SQLException { - String sql = "SELECT SUSER_SNAME()"; - - CallableStatement cstmt = connection.prepareCall(sql); - - if (1.8d <= javaVersion && 4 == major && 2 == minor) { - assertTrue(cstmt instanceof SQLServerCallableStatement42); - } - else { - assertTrue(cstmt instanceof SQLServerCallableStatement); - } - } - - @BeforeAll - private static void setupConnection() throws SQLException { - connection = DriverManager.getConnection(connectionString); - - DatabaseMetaData metadata = connection.getMetaData(); - major = metadata.getJDBCMajorVersion(); - minor = metadata.getJDBCMinorVersion(); - } - - @AfterAll - private static void terminateVariation() throws SQLException { - if (null != connection) { - connection.close(); - } - } - -} From 641a44a437b705d8ea8a1a4b2ed5a58796ca5f42 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Fri, 29 Jun 2018 12:04:44 -0700 Subject: [PATCH 83/84] 6.5.4 preview release changelog (#731) Release | Changelog for 6.5.4 preview release (#731) --- CHANGELOG.md | 20 +++++++++++++++++-- README.md | 10 +++++----- build.gradle | 4 ++-- pom.xml | 2 +- .../sqlserver/jdbc/SQLJdbcVersion.java | 2 +- 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5181e0c2..700f4a6e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) +## [6.5.4] Preview Release +### Added +- Added new connection property "useBulkCopyForBatchInsert" to enable Bulk Copy API support for batch insert operation [#686](https://github.com/Microsoft/mssql-jdbc/pull/686) +- Added implementation for Java 9 introduced Boundary methods APIs on Connection interface [#708](https://github.com/Microsoft/mssql-jdbc/pull/708) +- Added support for "Data Classification Specifications" on fetched resultsets [#709](https://github.com/Microsoft/mssql-jdbc/pull/709) +- Added support for UTF-8 feature extension [#722](https://github.com/Microsoft/mssql-jdbc/pull/722) + +### Fixed Issues +- Fixed issue with escaping catalog name when retrieving from database metadata [#718](https://github.com/Microsoft/mssql-jdbc/pull/718) +- Fixed issue with tests requiring additional dependencies [#729](https://github.com/Microsoft/mssql-jdbc/pull/729) + +### Changed +- Made driver default compliant to JDBC 4.2 specifications [#711](https://github.com/Microsoft/mssql-jdbc/pull/711) +- Updated ADAL4J dependency version to 1.6.0 [#711](https://github.com/Microsoft/mssql-jdbc/pull/711) +- Cleaned up socket handling implementation to generalize functionality for different JVMs and simplified the logic for single address case [#663](https://github.com/Microsoft/mssql-jdbc/pull/663) + ## [6.5.3] Preview Release ### Added - Added removed constructor back to AKV Provider which supports authentication with a customized method to fetch accessToken [#675](https://github.com/Microsoft/mssql-jdbc/pull/675) @@ -35,7 +51,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) ## [6.5.1] Preview Release ### Added -- Test cases for Date, Time, and Datetime2 data types. [#558](https://github.com/Microsoft/mssql-jdbc/pull/558) +- Test cases for Date, Time, and Datetime2 data types [#558](https://github.com/Microsoft/mssql-jdbc/pull/558) ### Fixed Issues - Fixed an issue where ResultSetMetadata returned incorrect columnType for Geometry and Geography data types [#657](https://github.com/Microsoft/mssql-jdbc/pull/657) @@ -93,7 +109,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) ## [6.3.4] Preview Release ### Added -- Added new ThreadGroup creation to prevent IllegalThreadStateException if the underlying ThreadGroup has been destroyed. [#474](https://github.com/Microsoft/mssql-jdbc/pull/474) +- Added new ThreadGroup creation to prevent IllegalThreadStateException if the underlying ThreadGroup has been destroyed [#474](https://github.com/Microsoft/mssql-jdbc/pull/474) - Added try-with-resources to JUnit tests [#520](https://github.com/Microsoft/mssql-jdbc/pull/520) ### Fixed Issues diff --git a/README.md b/README.md index 23ea036a4..ece0f9277 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ To get the latest preview version of the driver, add the following to your POM f com.microsoft.sqlserver mssql-jdbc - 6.5.3.jre10-preview + 6.5.4.jre10-preview ``` @@ -118,14 +118,14 @@ Projects that require either of the two features need to explicitly declare the com.microsoft.sqlserver mssql-jdbc - 6.5.3.jre10-preview + 6.5.4.jre10-preview compile com.microsoft.azure adal4j - 1.5.0 + 1.6.0 ``` @@ -134,14 +134,14 @@ Projects that require either of the two features need to explicitly declare the com.microsoft.sqlserver mssql-jdbc - 6.5.3.jre10-preview + 6.5.4.jre10-preview compile com.microsoft.azure adal4j - 1.5.0 + 1.6.0 diff --git a/build.gradle b/build.gradle index a3fd3a8c7..d6955d5c3 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ apply plugin: 'java' -version = '6.5.4-SNAPSHOT' +version = '6.5.4' def jreVersion = "" def testOutputDir = file("build/classes/java/test") def archivesBaseName = 'mssql-jdbc' @@ -79,7 +79,7 @@ repositories { dependencies { compile 'com.microsoft.azure:azure-keyvault:1.0.0', - 'com.microsoft.azure:adal4j:1.5.0' + 'com.microsoft.azure:adal4j:1.6.0' testCompile 'junit:junit:4.12', 'org.junit.platform:junit-platform-console:1.2.0', 'org.junit.platform:junit-platform-commons:1.2.0', diff --git a/pom.xml b/pom.xml index 1f0debdbe..8546b7cb8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.microsoft.sqlserver mssql-jdbc - 6.5.4-SNAPSHOT + 6.5.4 jar Microsoft JDBC Driver for SQL Server diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java index 964bb7ec9..41028eb8e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java @@ -11,6 +11,6 @@ final class SQLJdbcVersion { static final int major = 6; static final int minor = 5; - static final int patch = 3; + static final int patch = 4; static final int build = 0; } From f8b061ff4bbe7897a8e87402009c321d8dd63096 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Fri, 29 Jun 2018 17:06:27 -0700 Subject: [PATCH 84/84] Fix Conflict issues with master branch --- .../sqlserver/jdbc/SQLServerConnection.java | 25 +++---------------- .../jdbc/SQLServerPreparedStatement.java | 3 ++- .../ISQLServerBulkRecordIssuesTest.java | 2 ++ .../statement/BatchExecuteWithErrorsTest.java | 2 ++ .../unit/statement/PreparedStatementTest.java | 1 - 5 files changed, 10 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index b2111c923..0c4b2bef9 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -117,8 +117,6 @@ public class SQLServerConnection implements ISQLServerConnection, java.io.Serial private byte[] accessTokenInByte = null; private SqlFedAuthToken fedAuthToken = null; - - private String originalHostNameInCertificate = null; private String originalHostNameInCertificate = null; @@ -410,7 +408,7 @@ private enum State { ServerPortPlaceHolder getRoutingInfo() { return routingInfo; } - + // Permission targets private static final String callAbortPerm = "callAbort"; @@ -1206,23 +1204,6 @@ Connection connectInternal(Properties propsIn, activeConnectionProperties = (Properties) propsIn.clone(); pooledConnectionParent = pooledConnection; - - String hostNameInCertificate = activeConnectionProperties. - getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString()); - - // hostNameInCertificate property can change when redirection is involved, so maintain this value - // for every instance of SQLServerConnection. - if (null == originalHostNameInCertificate && null != hostNameInCertificate && !hostNameInCertificate.isEmpty()) { - originalHostNameInCertificate = activeConnectionProperties. - getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString()); - } - - if (null != originalHostNameInCertificate && !originalHostNameInCertificate.isEmpty()) { - // if hostNameInCertificate has a legitimate value (and not empty or null), - // reset hostNameInCertificate to the original value every time we connect (or re-connect). - activeConnectionProperties.setProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString(), - originalHostNameInCertificate); - } String hostNameInCertificate = activeConnectionProperties.getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString()); @@ -3519,7 +3500,8 @@ private void logon(LogonCommand command) throws SQLServerException { connectionCommand(sqlStmt, "Change Settings"); } } - } finally { + } + finally { if (integratedSecurity) { if (null != authentication) { authentication.ReleaseClientContext(); @@ -3741,6 +3723,7 @@ final void processEnvChange(TDSReader tdsReader) throws SQLServerException { isRoutedInCurrentAttempt = true; routingInfo = new ServerPortPlaceHolder(routingServerName, routingPortNumber, null, integratedSecurity); + break; // Error on unrecognized, unused ENVCHANGES diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index cf4578fe5..063dabe39 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -166,7 +166,7 @@ private boolean resetPrepStmtHandle(boolean discardCurrentCacheItem) { prepStmtHandle = 0; return statementPoolingUsed; } - + /** Flag set to true when statement execution is expected to return the prepared statement handle */ private boolean expectPrepStmtHandle = false; @@ -252,6 +252,7 @@ private void closePreparedHandle() { else { isExecutedAtLeastOnce = false; final int handleToClose = prepStmtHandle; + // Handle unprepare actions through statement pooling. if (resetPrepStmtHandle(false)) { connection.returnCachedPreparedStatementHandle(cachedPreparedStatementHandle); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/ISQLServerBulkRecordIssuesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/ISQLServerBulkRecordIssuesTest.java index 38ae1b4d0..bb24c65dc 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/ISQLServerBulkRecordIssuesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/ISQLServerBulkRecordIssuesTest.java @@ -42,6 +42,7 @@ @RunWith(JUnitPlatform.class) public class ISQLServerBulkRecordIssuesTest extends AbstractTest { + static Statement stmt = null; static PreparedStatement pStmt = null; static String query; @@ -271,6 +272,7 @@ public static void afterAllTests() throws SQLException { con.close(); } } + } class BulkData implements ISQLServerBulkRecord { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java index 550369b4c..e3659adfc 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java @@ -297,10 +297,12 @@ private void Repro47239Internal(String mode) throws Exception { catch (Exception ignored) { } } + } } private void Repro47239largeInternal(String mode) throws Exception { + assumeTrue("JDBC42".equals(Utils.getConfiguredProperty("JDBC_Version")), TestResource.getResource("R_incompatJDBC")); // the DBConnection for detecting whether the server is SQL Azure or SQL Server. con = DriverManager.getConnection(connectionString); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java index 96cb920ac..78b2777b0 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java @@ -136,7 +136,6 @@ public void testBatchedUnprepare() throws SQLException { @Tag("slow") public void testStatementPooling() throws Exception { testStatementPoolingInternal("batchInsert"); - } /**