diff --git a/.gitignore b/.gitignore index acb9b8d91..b8095970d 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,8 @@ local.properties .settings/ .gradle/ .loadpath +outdated-dependencies.txt +pom.xml.versionBackup # External tool builders .externalToolBuilders/ diff --git a/.travis.yml b/.travis.yml index 47c919414..a9a2d8f11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,13 @@ language: java jdk: - - oraclejdk8 + - oraclejdk9 +addons: + apt: + packages: + - oracle-java9-installer + services: - docker @@ -29,7 +34,7 @@ install: - keytool -importkeystore -destkeystore clientcert.jks -deststorepass password -srckeystore identity.p12 -srcstoretype PKCS12 -srcstorepass password - keytool -list -v -keystore clientcert.jks -storepass "password" > JavaKeyStore.txt - cd .. - - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pbuild41 + - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pbuild43 - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pbuild42 before_script: @@ -39,6 +44,6 @@ before_script: script: - docker ps -a -##Test for JDBC Specification 41 & 42 and submit coverage report. - - mvn test -B -Pbuild41 jacoco:report && bash <(curl -s https://codecov.io/bash) -cF JDBC41 +##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 -Pbuild42 jacoco:report && bash <(curl -s https://codecov.io/bash) -cF JDBC42 diff --git a/README.md b/README.md index d8198585b..6d2d0a746 100644 --- a/README.md +++ b/README.md @@ -36,19 +36,19 @@ What's coming next? We will look into adding a more comprehensive set of tests, ## Build ### Prerequisites -* Java 8 +* 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://msdn.microsoft.com/en-us/library/ms378428(v=sql.110).aspx) for your SQL Server or Azure SQL Database instance. -To build the jar files, you must use Java 8 with Maven. You can choose to build a JDBC 4.1 compliant jar file (for use with JRE 7) and/or a JDBC 4.2 compliant jar file (for use with JRE 8). +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.1 compliant jar or JDBC 4.2 compliant jar in the \target directory. - * Run `mvn install -Pbuild41`. This creates JDBC 4.1 compliant jar in \target directory + 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 **NOTE**: Beginning release v6.1.7, we will no longer be maintaining the existing [Gradle build script](build.gradle) and it will be left in the repository for reference. Please refer to issue [#62](https://github.com/Microsoft/mssql-jdbc/issues/62) for this decision. @@ -100,8 +100,8 @@ To get the latest preview version of the driver, add the following to your POM f This project has following dependencies: Compile Time: - - `azure-keyvault` : Azure Key Vault Provider for Always Encrypted feature (optional) - - `adal4j` : Azure ActiveDirectory Library for Java for Azure Active Directory Authentication feature (optional) + - `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. @@ -131,7 +131,7 @@ Projects that require either of the two features need to explicitly declare the ``` -***For Example:*** If you are using *Azure Key Vault feature* then you need to redeclare *azure-keyvault* dependency in your project's pom file. Please see the following snippet: +***For Example:*** If you are using *Azure Key Vault feature* then you need to redeclare *azure-keyvault* dependency and *adal4j* dependency in your project's pom file. Please see the following snippet: ```xml com.microsoft.sqlserver @@ -140,6 +140,12 @@ Projects that require either of the two features need to explicitly declare the compile + + com.microsoft.azure + adal4j + 1.3.0 + + com.microsoft.azure azure-keyvault diff --git a/appveyor.yml b/appveyor.yml index cbd4229fe..8caae887f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -32,9 +32,9 @@ build_script: - 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 -Pbuild41 - - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pbuild42 + # - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pbuild41 + # - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pbuild42 -test_script: - - mvn test -B -Pbuild41 - - mvn test -B -Pbuild42 +#test_script: +# - mvn test -B -Pbuild41 +# - mvn test -B -Pbuild42 diff --git a/maven-version-rules.xml b/maven-version-rules.xml new file mode 100644 index 000000000..6d126d278 --- /dev/null +++ b/maven-version-rules.xml @@ -0,0 +1,26 @@ + + + + + (?i).*Alpha(?:-?\d+)? + (?i).*Beta(?:-?\d+)? + (?i).*-B(?:-?\d+)? + (?i).*RC(?:-?\d+)? + (?i).*EA(?:-?\d+)? + (?i).*SNAPSHOT(?:-?\d+)? + + (?i).*M(?:-?\d+)? + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 58aa5d4ab..c6e459c4e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,11 @@ + 4.0.0 com.microsoft.sqlserver mssql-jdbc - 6.3.6.${jreVersion}-preview + 6.4.0-SNAPSHOT.${jreVersion} jar Microsoft JDBC Driver for SQL Server @@ -54,7 +55,7 @@ com.microsoft.azure adal4j - 1.3.0 + 1.4.0 true @@ -117,7 +118,7 @@ com.zaxxer HikariCP - 2.6.1 + 2.7.4 test @@ -138,10 +139,10 @@ - build41 + build42 - jre7 + jre8 @@ -151,10 +152,10 @@ 3.6.0 - **/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java + **/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java - 1.7 - 1.7 + 1.8 + 1.8 @@ -166,21 +167,19 @@ ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - + - + - build42 - + build43 true - jre8 + jre9 @@ -190,10 +189,10 @@ 3.6.0 - **/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java + **/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java - 1.8 - 1.8 + 9 + 9 @@ -278,7 +277,7 @@ org.apache.felix maven-bundle-plugin - 3.2.0 + 3.3.0 true @@ -300,7 +299,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.10.4 + 3.0.0-M1 true @@ -335,6 +334,16 @@ + + + org.codehaus.mojo + versions-maven-plugin + true + + outdated-dependencies.txt + file:///${session.executionRootDirectory}/maven-version-rules.xml + + diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java b/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java index d86d481f4..112aac152 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java @@ -41,6 +41,10 @@ final class AuthenticationJNI extends SSPIAuthentication { static int GetMaxSSPIBlobSize() { return sspiBlobMaxlen; } + + static boolean isDllLoaded() { + return enabled; + } static { UnsatisfiedLinkError temp = null; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/DDC.java b/src/main/java/com/microsoft/sqlserver/jdbc/DDC.java index 77891c37b..1e90ef726 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/DDC.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/DDC.java @@ -232,7 +232,7 @@ static final Object convertFloatToObject(float floatVal, return new BigDecimal(Float.toString(floatVal)); case FLOAT: case DOUBLE: - return (new Float(floatVal)).doubleValue(); + return (Float.valueOf(floatVal)).doubleValue(); case BINARY: return convertIntToBytes(Float.floatToRawIntBits(floatVal), 4); default: @@ -275,7 +275,7 @@ static final Object convertDoubleToObject(double doubleVal, case DOUBLE: return doubleVal; case REAL: - return (new Double(doubleVal)).floatValue(); + return (Double.valueOf(doubleVal)).floatValue(); case INTEGER: return (int) doubleVal; case SMALLINT: // small and tinyint returned as short @@ -439,7 +439,7 @@ private static byte[] convertToBytes(BigDecimal value, } } int offset = numBytes - unscaledBytes.length; - System.arraycopy(unscaledBytes, offset - offset, ret, offset, numBytes - offset); + System.arraycopy(unscaledBytes, 0, ret, offset, numBytes - offset); return ret; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/FailOverInfo.java b/src/main/java/com/microsoft/sqlserver/jdbc/FailOverInfo.java index 0e905bc7d..cdfe4fb83 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/FailOverInfo.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/FailOverInfo.java @@ -71,7 +71,7 @@ private void setupInfo(SQLServerConnection con) throws SQLServerException { instancePort = con.getInstancePort(failoverPartner, instanceValue); try { - portNumber = new Integer(instancePort); + portNumber = Integer.parseInt(instancePort); } catch (NumberFormatException e) { // Should not get here as the server should give a proper port number anyway. diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index d87a9cb14..82513ea17 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -73,7 +73,7 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; -import javax.xml.bind.DatatypeConverter; +import java.nio.Buffer; final class TDS { // TDS protocol versions @@ -670,7 +670,7 @@ void disableSSL() { * The mission: To close the SSLSocket and release everything that it is holding onto other than the TCP/IP socket and streams. * * The challenge: Simply closing the SSLSocket tries to do additional, unnecessary shutdown I/O over the TCP/IP streams that are bound to the - * socket proxy, resulting in a hang and confusing SQL Server. + * socket proxy, resulting in a not responding and confusing SQL Server. * * Solution: Rewire the ProxySocket's input and output streams (one more time) to closed streams. SSLSocket sees that the streams are already * closed and does not attempt to do any further I/O on them before closing itself. @@ -1870,13 +1870,11 @@ private void validateFips(final String trustStoreType, if (isEncryptOn && !isTrustServerCertificate) { isValid = true; - if (isValidTrustStore) { - // In case of valid trust store we need to check TrustStoreType. - if (!isValidTrustStoreType) { - isValid = false; - if (logger.isLoggable(Level.FINER)) - logger.finer(toString() + "TrustStoreType is required alongside with TrustStore."); - } + if (isValidTrustStore && !isValidTrustStoreType) { + // In case of valid trust store we need to check TrustStoreType. + isValid = false; + if (logger.isLoggable(Level.FINER)) + logger.finer(toString() + "TrustStoreType is required alongside with TrustStore."); } } @@ -2547,7 +2545,7 @@ private void findSocketUsingJavaNIO(InetAddress[] inetAddrs, + " occured while processing the channel: " + ch); updateSelectedException(ex, this.toString()); // close the channel pro-actively so that we do not - // hang on to network resources + // rely to network resources ch.close(); } @@ -2894,11 +2892,8 @@ void updateResult(Socket socket, public void updateSelectedException(IOException ex, String traceId) { boolean updatedException = false; - if (selectedException == null) { - selectedException = ex; - updatedException = true; - } - else if ((!(ex instanceof SocketTimeoutException)) && (selectedException instanceof SocketTimeoutException)) { + if (selectedException == null || + (!(ex instanceof SocketTimeoutException)) && (selectedException instanceof SocketTimeoutException)) { selectedException = ex; updatedException = true; } @@ -3112,7 +3107,7 @@ boolean isEOMSent() { void preparePacket() throws SQLServerException { if (tdsChannel.isLoggingPackets()) { Arrays.fill(logBuffer.array(), (byte) 0xFE); - logBuffer.clear(); + ((Buffer)logBuffer).clear(); } // Write a placeholder packet header. This will be replaced @@ -3188,8 +3183,8 @@ void startMessage(TDSCommand command, currentPacketSize = negotiatedPacketSize; } - socketBuffer.position(socketBuffer.limit()); - stagingBuffer.clear(); + ((Buffer) socketBuffer).position(((Buffer) socketBuffer).limit()); + ((Buffer)stagingBuffer).clear(); preparePacket(); writeMessageHeader(); @@ -3231,7 +3226,7 @@ void writeByte(byte value) throws SQLServerException { if (dataIsLoggable) logBuffer.put(value); else - logBuffer.position(logBuffer.position() + 1); + ((Buffer)logBuffer).position(((Buffer)logBuffer).position() + 1); } } else { @@ -3258,7 +3253,7 @@ void writeChar(char value) throws SQLServerException { if (dataIsLoggable) logBuffer.putChar(value); else - logBuffer.position(logBuffer.position() + 2); + ((Buffer)logBuffer).position( ((Buffer)logBuffer).position() + 2); } } else { @@ -3274,7 +3269,7 @@ void writeShort(short value) throws SQLServerException { if (dataIsLoggable) logBuffer.putShort(value); else - logBuffer.position(logBuffer.position() + 2); + ((Buffer)logBuffer).position( ((Buffer)logBuffer).position() + 2); } } else { @@ -3290,7 +3285,7 @@ void writeInt(int value) throws SQLServerException { if (dataIsLoggable) logBuffer.putInt(value); else - logBuffer.position(logBuffer.position() + 4); + ((Buffer)logBuffer).position( ((Buffer)logBuffer).position() + 4); } } else { @@ -3322,7 +3317,7 @@ void writeDouble(double value) throws SQLServerException { if (dataIsLoggable) logBuffer.putDouble(value); else - logBuffer.position(logBuffer.position() + 8); + ((Buffer)logBuffer).position( ((Buffer)logBuffer).position() + 8); } } else { @@ -3700,7 +3695,7 @@ void writeLong(long value) throws SQLServerException { if (dataIsLoggable) logBuffer.putLong(value); else - logBuffer.position(logBuffer.position() + 8); + ((Buffer)logBuffer).position( ((Buffer)logBuffer).position() + 8); } } else { @@ -3743,7 +3738,7 @@ void writeBytes(byte[] value, if (dataIsLoggable) logBuffer.put(value, offset + bytesWritten, bytesToWrite); else - logBuffer.position(logBuffer.position() + bytesToWrite); + ((Buffer)logBuffer).position( ((Buffer)logBuffer).position() + bytesToWrite); } bytesWritten += bytesToWrite; @@ -3770,7 +3765,7 @@ void writeWrappedBytes(byte value[], if (dataIsLoggable) logBuffer.put(value, 0, remaining); else - logBuffer.position(logBuffer.position() + remaining); + ((Buffer)logBuffer).position( ((Buffer)logBuffer).position() + remaining); } } @@ -3783,7 +3778,7 @@ void writeWrappedBytes(byte value[], if (dataIsLoggable) logBuffer.put(value, remaining, valueLength - remaining); else - logBuffer.position(logBuffer.position() + remaining); + ((Buffer)logBuffer).position( ((Buffer)logBuffer).position() + remaining); } } @@ -4105,7 +4100,7 @@ private void writePacket(int tdsMessageStatus) throws SQLServerException { } private void writePacketHeader(int tdsMessageStatus) { - int tdsMessageLength = stagingBuffer.position(); + int tdsMessageLength = ((Buffer)stagingBuffer).position(); ++packetNum; // Write the TDS packet header back at the start of the staging buffer @@ -4133,13 +4128,13 @@ private void writePacketHeader(int tdsMessageStatus) { void flush(boolean atEOM) throws SQLServerException { // First, flush any data left in the socket buffer. - tdsChannel.write(socketBuffer.array(), socketBuffer.position(), socketBuffer.remaining()); - socketBuffer.position(socketBuffer.limit()); + tdsChannel.write(socketBuffer.array(), ((Buffer)socketBuffer).position(), socketBuffer.remaining()); + ((Buffer)socketBuffer).position(((Buffer)socketBuffer).limit()); // If there is data in the staging buffer that needs to be written // to the socket, the socket buffer is now empty, so swap buffers // and start writing data from the staging buffer. - if (stagingBuffer.position() >= TDS_PACKET_HEADER_SIZE) { + if (((Buffer)stagingBuffer).position() >= TDS_PACKET_HEADER_SIZE) { // Swap the packet buffers ... ByteBuffer swapBuffer = stagingBuffer; stagingBuffer = socketBuffer; @@ -4151,14 +4146,14 @@ void flush(boolean atEOM) throws SQLServerException { // We need to use flip() rather than rewind() here so that // the socket buffer's limit is properly set for the last // packet, which may be shorter than the other packets. - socketBuffer.flip(); - stagingBuffer.clear(); + ((Buffer)socketBuffer).flip(); + ((Buffer)stagingBuffer).clear(); // If we are logging TDS packets then log the packet we're about // to send over the wire now. if (tdsChannel.isLoggingPackets()) { - tdsChannel.logPacket(logBuffer.array(), 0, socketBuffer.limit(), - this.toString() + " sending packet (" + socketBuffer.limit() + " bytes)"); + tdsChannel.logPacket(logBuffer.array(), 0, ((Buffer) socketBuffer).limit(), + this.toString() + " sending packet (" + ((Buffer) socketBuffer).limit() + " bytes)"); } // Prepare for the next packet @@ -4166,8 +4161,8 @@ void flush(boolean atEOM) throws SQLServerException { preparePacket(); // Finally, start sending data from the new socket buffer. - tdsChannel.write(socketBuffer.array(), socketBuffer.position(), socketBuffer.remaining()); - socketBuffer.position(socketBuffer.limit()); + tdsChannel.write(socketBuffer.array(), ((Buffer)socketBuffer).position(), socketBuffer.remaining()); + ((Buffer)socketBuffer).position( ((Buffer)socketBuffer).limit()); } } @@ -4630,7 +4625,7 @@ void writeTVPRows(TVP value) throws SQLServerException { if (con.equals(src_stmt.getConnection()) && 0 != resultSetServerCursorId) { cachedTVPHeaders = ByteBuffer.allocate(stagingBuffer.capacity()).order(stagingBuffer.order()); - cachedTVPHeaders.put(stagingBuffer.array(), 0, stagingBuffer.position()); + cachedTVPHeaders.put(stagingBuffer.array(), 0, ((Buffer)stagingBuffer).position()); cachedCommand = this.command; @@ -4656,9 +4651,9 @@ void writeTVPRows(TVP value) throws SQLServerException { if (tdsWritterCached) { command = cachedCommand; - stagingBuffer.clear(); - logBuffer.clear(); - writeBytes(cachedTVPHeaders.array(), 0, cachedTVPHeaders.position()); + ((Buffer)stagingBuffer).clear(); + ((Buffer)logBuffer).clear(); + writeBytes(cachedTVPHeaders.array(), 0, ((Buffer)cachedTVPHeaders).position()); } Object[] rowData = value.getRowData(); @@ -4957,7 +4952,7 @@ else if (DataTypes.UNKNOWN_STREAM_LENGTH == dataLength) isShortValue = columnPair.getValue().precision <= DataTypes.SHORT_VARTYPE_MAX_BYTES; isNull = (null == currentObject); if (currentObject instanceof String) - dataLength = isNull ? 0 : (toByteArray(currentObject.toString())).length; + dataLength = isNull ? 0 : (ParameterUtils.HexToBin(currentObject.toString())).length; else dataLength = isNull ? 0 : ((byte[]) currentObject).length; if (!isShortValue) { @@ -4976,7 +4971,7 @@ else if (DataTypes.UNKNOWN_STREAM_LENGTH == dataLength) if (dataLength > 0) { writeInt(dataLength); if (currentObject instanceof String) - writeBytes(toByteArray(currentObject.toString())); + writeBytes(ParameterUtils.HexToBin(currentObject.toString())); else writeBytes((byte[]) currentObject); } @@ -4990,7 +4985,7 @@ else if (DataTypes.UNKNOWN_STREAM_LENGTH == dataLength) else { writeShort((short) dataLength); if (currentObject instanceof String) - writeBytes(toByteArray(currentObject.toString())); + writeBytes(ParameterUtils.HexToBin(currentObject.toString())); else writeBytes((byte[]) currentObject); } @@ -5027,9 +5022,6 @@ private void writeTVPSqlVariantHeader(int length, writeByte(probBytes); } - private static byte[] toByteArray(String s) { - return DatatypeConverter.parseHexBinary(s); - } void writeTVPColumnMetaData(TVP value) throws SQLServerException { boolean isShortValue; @@ -7553,12 +7545,12 @@ final void checkForInterrupt() throws SQLServerException { * interrupted (0 or more packets sent with no EOM bit). */ final void onRequestComplete() throws SQLServerException { - assert !requestComplete; - - if (logger.isLoggable(Level.FINEST)) - logger.finest(this + ": request complete"); - synchronized (interruptLock) { + assert !requestComplete; + + if (logger.isLoggable(Level.FINEST)) + logger.finest(this + ": request complete"); + requestComplete = true; // If this command was interrupted before its request was complete then @@ -7630,8 +7622,8 @@ final void onResponseEOM() throws SQLServerException { // interrupting threads. Note that it is remotely possible that the call // to readPacket won't actually read anything if the attention ack was // already read by TDSCommand.detach(), in which case this method could - // be called from multiple threads, leading to a benign race to clear the - // readingResponse flag. + // be called from multiple threads, leading to a benign followup process + // to clear the readingResponse flag. if (readAttentionAck) tdsReader.readPacket(); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java new file mode 100644 index 000000000..1689cde87 --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java @@ -0,0 +1,19 @@ +package com.microsoft.sqlserver.jdbc; + +import java.sql.ShardingKey; + +public interface ISQLServerConnection43 extends ISQLServerConnection { + + 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/KerbAuthentication.java b/src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java index d7e7ab2cf..01ace7365 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java @@ -312,7 +312,7 @@ public boolean isRealmValid(String realm) { validator = oracleRealmValidator; // As explained here: https://github.com/Microsoft/mssql-jdbc/pull/40#issuecomment-281509304 // The default Oracle Resolution mechanism is not bulletproof - // If it resolves a crappy name, drop it. + // If it resolves a non-existing name, drop it. if (!validator.isRealmValid("this.might.not.exist." + hostnameToTest)) { // Our realm validator is well working, return it authLogger.fine("Kerberos Realm Validator: Using Built-in Oracle Realm Validation method."); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java b/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java index 2667d5af0..1a2513816 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java @@ -11,6 +11,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.Reader; +import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; @@ -182,7 +183,7 @@ private boolean encodeChars() throws IOException { } else { // Flip the buffer to be ready for put (reader read) operations. - rawChars.clear(); + ((Buffer)rawChars).clear(); } // Try to fill up the raw character buffer by reading available characters @@ -194,7 +195,7 @@ private boolean encodeChars() throws IOException { // - the reader throws any kind of Exception (driver throws an IOException) // - the reader violates its interface contract (driver throws an IOException) while (rawChars.hasRemaining()) { - int lastPosition = rawChars.position(); + int lastPosition = ((Buffer)rawChars).position(); int charsRead = 0; // Try reading from the app-supplied Reader @@ -218,7 +219,7 @@ private boolean encodeChars() throws IOException { if (-1 == charsRead) { // If the reader violates its interface contract then throw an exception. - if (rawChars.position() != lastPosition) + if (((Buffer)rawChars).position() != lastPosition) throw new IOException(SQLServerException.getErrString("R_streamReadReturnedInvalidValue")); // Check that the reader has returned exactly the amount of data we expect @@ -228,7 +229,7 @@ private boolean encodeChars() throws IOException { } // If there are no characters left to encode then we're done. - if (0 == rawChars.position()) { + if (0 == ((Buffer)rawChars).position()) { rawChars = null; atEndOfStream = true; return false; @@ -241,7 +242,7 @@ private boolean encodeChars() throws IOException { assert charsRead > 0; // If the reader violates its interface contract then throw an exception. - if (charsRead != rawChars.position() - lastPosition) + if (charsRead != ((Buffer)rawChars).position() - lastPosition) throw new IOException(SQLServerException.getErrString("R_streamReadReturnedInvalidValue")); // Check that the reader isn't trying to return more data than we expect @@ -255,7 +256,7 @@ private boolean encodeChars() throws IOException { // The raw character buffer may now have characters available for encoding. // Flip the buffer back to be ready for get (charset encode) operations. - rawChars.flip(); + ((Buffer)rawChars).flip(); } // If the raw character buffer remains empty, despite our efforts to (re)populate it, diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java index e3e10dd06..096543ffd 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java @@ -1,11 +1,15 @@ package com.microsoft.sqlserver.jdbc; +import java.io.IOException; import java.net.MalformedURLException; import java.text.MessageFormat; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.logging.Level; + +import javax.security.auth.kerberos.KerberosPrincipal; import com.microsoft.aad.adal4j.AuthenticationContext; import com.microsoft.aad.adal4j.AuthenticationException; @@ -15,10 +19,13 @@ class SQLServerADAL4JUtils { + static final private java.util.logging.Logger adal4jLogger = java.util.logging.Logger + .getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerADAL4JUtils"); + static SqlFedAuthToken getSqlFedAuthToken(SqlFedAuthInfo fedAuthInfo, - String user, - String password, - String authenticationString) throws SQLServerException { + String user, + String password, + String authenticationString) throws SQLServerException { ExecutorService executorService = Executors.newFixedThreadPool(1); try { AuthenticationContext context = new AuthenticationContext(fedAuthInfo.stsurl, false, executorService); @@ -42,7 +49,8 @@ static SqlFedAuthToken getSqlFedAuthToken(SqlFedAuthInfo fedAuthInfo, String correctedErrorMessage = e.getCause().getMessage().replaceAll("\\\\r\\\\n", "\r\n"); AuthenticationException correctedAuthenticationException = new AuthenticationException(correctedErrorMessage); - // SQLServerException is caused by ExecutionException, which is caused by AuthenticationException + // SQLServerException is caused by ExecutionException, which is caused by + // AuthenticationException // to match the exception tree before error message correction ExecutionException correctedExecutionException = new ExecutionException(correctedAuthenticationException); @@ -52,4 +60,57 @@ static SqlFedAuthToken getSqlFedAuthToken(SqlFedAuthInfo fedAuthInfo, executorService.shutdown(); } } + + static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, + String authenticationString) throws SQLServerException { + ExecutorService executorService = Executors.newFixedThreadPool(1); + + try { + // principal name does not matter, what matters is the realm name + // it gets the username in principal_name@realm_name format + KerberosPrincipal kerberosPrincipal = new KerberosPrincipal("username"); + String username = kerberosPrincipal.getName(); + + if (adal4jLogger.isLoggable(Level.FINE)) { + adal4jLogger.fine(adal4jLogger.toString() + " realm name is:" + kerberosPrincipal.getRealm()); + } + + AuthenticationContext context = new AuthenticationContext(fedAuthInfo.stsurl, false, executorService); + Future future = context.acquireToken(fedAuthInfo.spn, ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, + username, null, null); + + AuthenticationResult authenticationResult = future.get(); + SqlFedAuthToken fedAuthToken = new SqlFedAuthToken(authenticationResult.getAccessToken(), authenticationResult.getExpiresOnDate()); + + return fedAuthToken; + } + catch (InterruptedException | IOException e) { + throw new SQLServerException(e.getMessage(), e); + } + catch (ExecutionException e) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution")); + Object[] msgArgs = {"", authenticationString}; + + if (null == e.getCause() || null == e.getCause().getMessage()) { + // the case when Future's outcome has no AuthenticationResult but exception + throw new SQLServerException(form.format(msgArgs), null); + } + else { + // the cause error message uses \\n\\r which does not give correct format + // change it to \r\n to provide correct format + String correctedErrorMessage = e.getCause().getMessage().replaceAll("\\\\r\\\\n", "\r\n"); + AuthenticationException correctedAuthenticationException = new AuthenticationException(correctedErrorMessage); + + // SQLServerException is caused by ExecutionException, which is caused by + // AuthenticationException + // to match the exception tree before error message correction + ExecutionException correctedExecutionException = new ExecutionException(correctedAuthenticationException); + + throw new SQLServerException(form.format(msgArgs), null, 0, correctedExecutionException); + } + } + finally { + executorService.shutdown(); + } + } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java index 8f71ec6b6..3a170da2d 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java @@ -12,8 +12,7 @@ import java.text.MessageFormat; import java.util.concurrent.ConcurrentHashMap; - -import javax.xml.bind.DatatypeConverter; +import java.util.Base64; /** * Factory for SQLServerAeadAes256CbcHmac256Algorithm @@ -38,7 +37,7 @@ SQLServerEncryptionAlgorithm create(SQLServerSymmetricKey columnEncryptionKey, } StringBuilder factoryKeyBuilder = new StringBuilder(); - factoryKeyBuilder.append(DatatypeConverter.printBase64Binary(new String(columnEncryptionKey.getRootKey(), UTF_8).getBytes())); + factoryKeyBuilder.append(Base64.getEncoder().encodeToString(new String(columnEncryptionKey.getRootKey(), UTF_8).getBytes())); factoryKeyBuilder.append(":"); factoryKeyBuilder.append(encryptionType); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java index 4ff063dab..cce49a416 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.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -575,7 +574,7 @@ public Object[] getRowData() throws SQLServerException { case Types.BIGINT: { BigDecimal bd = new BigDecimal(data[pair.getKey() - 1].trim()); try { - dataRow[pair.getKey() - 1] = bd.setScale(0, BigDecimal.ROUND_DOWN).longValueExact(); + 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")); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java index a383e1946..50f711c29 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java @@ -327,7 +327,7 @@ public void run() { public SQLServerBulkCopy(Connection connection) throws SQLServerException { loggerExternal.entering(loggerClassName, "SQLServerBulkCopy", connection); - if (null == connection || !connection.getClass().equals(SQLServerConnection.class)) { + if (null == connection || !(connection instanceof SQLServerConnection)) { SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDestConnection"), null, false); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionCertificateStoreProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionCertificateStoreProvider.java index 91a473b73..5f8bc8001 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionCertificateStoreProvider.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionCertificateStoreProvider.java @@ -22,10 +22,10 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.text.MessageFormat; +import java.util.Base64; import java.util.Enumeration; import java.util.Locale; -import javax.xml.bind.DatatypeConverter; /** * The implementation of the key store provider for the Windows Certificate Store. This class enables using keys stored in the Windows Certificate @@ -140,7 +140,7 @@ private String getThumbPrint(X509Certificate cert) throws NoSuchAlgorithmExcepti byte[] der = cert.getEncoded(); md.update(der); byte[] digest = md.digest(); - return DatatypeConverter.printHexBinary(digest); + return Base64.getEncoder().encodeToString(digest); } private CertificateDetails getCertificateByThumbprint(String storeLocation, diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index e883f9a75..c8232d71a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -1423,7 +1423,7 @@ Connection connectInternal(Properties propsIn, sPropKey = SQLServerDriverIntProperty.STATEMENT_POOLING_CACHE_SIZE.toString(); if (activeConnectionProperties.getProperty(sPropKey) != null && activeConnectionProperties.getProperty(sPropKey).length() > 0) { try { - int n = new Integer(activeConnectionProperties.getProperty(sPropKey)); + int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey)); this.setStatementPoolingCacheSize(n); } catch (NumberFormatException e) { @@ -1539,11 +1539,6 @@ Connection connectInternal(Properties propsIn, throw new SQLServerException(SQLServerException.getErrString("R_AccessTokenWithUserPassword"), null); } - if ((!System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows")) - && (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString()))) { - throw new SQLServerException(SQLServerException.getErrString("R_AADIntegratedOnNonWindows"), null); - } - // Turn off TNIR for FedAuth if user does not set TNIR explicitly if (!userSetTNIR) { if ((!authenticationString.equalsIgnoreCase(SqlAuthentication.NotSpecified.toString())) || (null != accessTokenInByte)) { @@ -1560,7 +1555,7 @@ Connection connectInternal(Properties propsIn, try { String strPort = activeConnectionProperties.getProperty(sPropKey); if (null != strPort) { - nPort = new Integer(strPort); + nPort = Integer.parseInt(strPort); if ((nPort < 0) || (nPort > 65535)) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPortNumber")); @@ -1640,7 +1635,7 @@ else if (0 == requestedPacketSize) nLockTimeout = defaultLockTimeOut; // Wait forever if (activeConnectionProperties.getProperty(sPropKey) != null && activeConnectionProperties.getProperty(sPropKey).length() > 0) { try { - int n = new Integer(activeConnectionProperties.getProperty(sPropKey)); + int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey)); if (n >= defaultLockTimeOut) nLockTimeout = n; else { @@ -1661,7 +1656,7 @@ else if (0 == requestedPacketSize) queryTimeoutSeconds = defaultQueryTimeout; // Wait forever if (activeConnectionProperties.getProperty(sPropKey) != null && activeConnectionProperties.getProperty(sPropKey).length() > 0) { try { - int n = new Integer(activeConnectionProperties.getProperty(sPropKey)); + int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey)); if (n >= defaultQueryTimeout) { queryTimeoutSeconds = n; } @@ -1683,7 +1678,7 @@ else if (0 == requestedPacketSize) socketTimeoutMilliseconds = defaultSocketTimeout; // Wait forever if (activeConnectionProperties.getProperty(sPropKey) != null && activeConnectionProperties.getProperty(sPropKey).length() > 0) { try { - int n = new Integer(activeConnectionProperties.getProperty(sPropKey)); + int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey)); if (n >= defaultSocketTimeout) { socketTimeoutMilliseconds = n; } @@ -1703,7 +1698,7 @@ else if (0 == requestedPacketSize) sPropKey = SQLServerDriverIntProperty.SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD.toString(); if (activeConnectionProperties.getProperty(sPropKey) != null && activeConnectionProperties.getProperty(sPropKey).length() > 0) { try { - int n = new Integer(activeConnectionProperties.getProperty(sPropKey)); + int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey)); setServerPreparedStatementDiscardThreshold(n); } catch (NumberFormatException e) { @@ -2164,7 +2159,7 @@ ServerPortPlaceHolder primaryPermissionCheck(String primary, connectionlogger.fine(toString() + " SQL Server port returned by SQL Browser: " + instancePort); try { if (null != instancePort) { - primaryPortNumber = new Integer(instancePort); + primaryPortNumber = Integer.parseInt(instancePort); if ((primaryPortNumber < 0) || (primaryPortNumber > 65535)) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPortNumber")); @@ -3186,8 +3181,9 @@ public PreparedStatement prepareStatement(String sql, checkClosed(); PreparedStatement st; - - if (Util.use42Wrapper()) { + + // Make sure SQLServerPreparedStatement42 is used for 4.2 and above. + if (Util.use42Wrapper() || Util.use43Wrapper()) { st = new SQLServerPreparedStatement42(this, sql, resultSetType, resultSetConcurrency, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); } @@ -3211,7 +3207,8 @@ private PreparedStatement prepareStatement(String sql, PreparedStatement st; - if (Util.use42Wrapper()) { + // Make sure SQLServerPreparedStatement42 is used for 4.2 and above. + if (Util.use42Wrapper() || Util.use43Wrapper()) { st = new SQLServerPreparedStatement42(this, sql, resultSetType, resultSetConcurrency, stmtColEncSetting); } else { @@ -3232,7 +3229,8 @@ public CallableStatement prepareCall(String sql, CallableStatement st; - if (Util.use42Wrapper()) { + // Make sure SQLServerCallableStatement42 is used for 4.2 and above. + if (Util.use42Wrapper() || Util.use43Wrapper()) { st = new SQLServerCallableStatement42(this, sql, resultSetType, resultSetConcurrency, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); } @@ -3855,18 +3853,13 @@ private SqlFedAuthToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throws SQLSe // fedAuthInfo should not be null. assert null != fedAuthInfo; - // No:of milliseconds to sleep for the inital back off. - int sleepInterval = 100; - - // No:of attempts, for tracing purposes, if we underwent retries. - int numberOfAttempts = 0; - String user = activeConnectionProperties.getProperty(SQLServerDriverStringProperty.USER.toString()); String password = activeConnectionProperties.getProperty(SQLServerDriverStringProperty.PASSWORD.toString()); + // No:of milliseconds to sleep for the inital back off. + int sleepInterval = 100; + while (true) { - numberOfAttempts++; - if (authenticationString.trim().equalsIgnoreCase(SqlAuthentication.ActiveDirectoryPassword.toString())) { fedAuthToken = SQLServerADAL4JUtils.getSqlFedAuthToken(fedAuthInfo, user, password, authenticationString); @@ -3874,69 +3867,80 @@ private SqlFedAuthToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throws SQLSe break; } else if (authenticationString.trim().equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString())) { - try { - long expirationFileTime = 0; - FedAuthDllInfo dllInfo = AuthenticationJNI.getAccessTokenForWindowsIntegrated(fedAuthInfo.stsurl, fedAuthInfo.spn, - clientConnectionId.toString(), ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, expirationFileTime); + + // 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 { + long expirationFileTime = 0; + FedAuthDllInfo dllInfo = AuthenticationJNI.getAccessTokenForWindowsIntegrated(fedAuthInfo.stsurl, fedAuthInfo.spn, + clientConnectionId.toString(), ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, expirationFileTime); - // AccessToken should not be null. - assert null != dllInfo.accessTokenBytes; + // AccessToken should not be null. + assert null != dllInfo.accessTokenBytes; - byte[] accessTokenFromDLL = dllInfo.accessTokenBytes; + byte[] accessTokenFromDLL = dllInfo.accessTokenBytes; - String accessToken = new String(accessTokenFromDLL, UTF_16LE); + String accessToken = new String(accessTokenFromDLL, UTF_16LE); - fedAuthToken = new SqlFedAuthToken(accessToken, dllInfo.expiresIn); + fedAuthToken = new SqlFedAuthToken(accessToken, dllInfo.expiresIn); - // Break out of the retry loop in successful case. - break; - } - catch (DLLException adalException) { - - // the sqljdbc_auth.dll return -1 for errorCategory, if unable to load the adalsql.dll - int errorCategory = adalException.GetCategory(); - if (-1 == errorCategory) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_UnableLoadADALSqlDll")); - Object[] msgArgs = {Integer.toHexString(adalException.GetState())}; - throw new SQLServerException(form.format(msgArgs), null); + // Break out of the retry loop in successful case. + break; } + catch (DLLException adalException) { + + // the sqljdbc_auth.dll return -1 for errorCategory, if unable to load the adalsql.dll + int errorCategory = adalException.GetCategory(); + if (-1 == errorCategory) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_UnableLoadADALSqlDll")); + Object[] msgArgs = {Integer.toHexString(adalException.GetState())}; + throw new SQLServerException(form.format(msgArgs), null); + } - int millisecondsRemaining = TimerRemaining(timerExpire); - if (ActiveDirectoryAuthentication.GET_ACCESS_TOKEN_TANSISENT_ERROR != errorCategory || timerHasExpired(timerExpire) - || (sleepInterval >= millisecondsRemaining)) { + int millisecondsRemaining = TimerRemaining(timerExpire); + if (ActiveDirectoryAuthentication.GET_ACCESS_TOKEN_TANSISENT_ERROR != errorCategory || timerHasExpired(timerExpire) + || (sleepInterval >= millisecondsRemaining)) { - String errorStatus = Integer.toHexString(adalException.GetStatus()); + String errorStatus = Integer.toHexString(adalException.GetStatus()); - if (connectionlogger.isLoggable(Level.FINER)) { - connectionlogger.fine(toString() + " SQLServerConnection.getFedAuthToken.AdalException category:" + errorCategory - + " error: " + errorStatus); - } + if (connectionlogger.isLoggable(Level.FINER)) { + connectionlogger.fine(toString() + " SQLServerConnection.getFedAuthToken.AdalException category:" + errorCategory + + " error: " + errorStatus); + } - MessageFormat form1 = new MessageFormat(SQLServerException.getErrString("R_ADALAuthenticationMiddleErrorMessage")); - String errorCode = Integer.toHexString(adalException.GetStatus()).toUpperCase(); - Object[] msgArgs1 = {errorCode, adalException.GetState()}; - SQLServerException middleException = new SQLServerException(form1.format(msgArgs1), adalException); + MessageFormat form1 = new MessageFormat(SQLServerException.getErrString("R_ADALAuthenticationMiddleErrorMessage")); + String errorCode = Integer.toHexString(adalException.GetStatus()).toUpperCase(); + Object[] msgArgs1 = {errorCode, adalException.GetState()}; + SQLServerException middleException = new SQLServerException(form1.format(msgArgs1), adalException); - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution")); - Object[] msgArgs = {user, authenticationString}; - throw new SQLServerException(form.format(msgArgs), null, 0, middleException); - } + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution")); + Object[] msgArgs = {user, authenticationString}; + throw new SQLServerException(form.format(msgArgs), null, 0, middleException); + } - if (connectionlogger.isLoggable(Level.FINER)) { - connectionlogger.fine(toString() + " SQLServerConnection.getFedAuthToken sleeping: " + sleepInterval + " milliseconds."); - connectionlogger - .fine(toString() + " SQLServerConnection.getFedAuthToken remaining: " + millisecondsRemaining + " milliseconds."); - } + if (connectionlogger.isLoggable(Level.FINER)) { + connectionlogger.fine(toString() + " SQLServerConnection.getFedAuthToken sleeping: " + sleepInterval + " milliseconds."); + connectionlogger + .fine(toString() + " SQLServerConnection.getFedAuthToken remaining: " + millisecondsRemaining + " milliseconds."); + } - try { - Thread.sleep(sleepInterval); - } - catch (InterruptedException e1) { - // re-interrupt the current thread, in order to restore the thread's interrupt status. - Thread.currentThread().interrupt(); + try { + Thread.sleep(sleepInterval); + } + catch (InterruptedException e1) { + // re-interrupt the current thread, in order to restore the thread's interrupt status. + Thread.currentThread().interrupt(); + } + sleepInterval = sleepInterval * 2; } - sleepInterval = sleepInterval * 2; } + // else choose ADAL4J for integrated authentication. This option is supported for both windows and unix, so we don't need to check the + // OS version here. + else { + fedAuthToken = SQLServerADAL4JUtils.getSqlFedAuthTokenIntegrated(fedAuthInfo, authenticationString); + } + // Break out of the retry loop in successful case. + break; } } @@ -4655,7 +4659,8 @@ public PreparedStatement prepareStatement(java.lang.String sql, PreparedStatement st; - if (Util.use42Wrapper()) { + // Make sure SQLServerPreparedStatement42 is used for 4.2 and above. + if (Util.use42Wrapper() || Util.use43Wrapper()) { st = new SQLServerPreparedStatement42(this, sql, nType, nConcur, stmtColEncSetting); } else { @@ -4690,7 +4695,8 @@ public CallableStatement prepareCall(String sql, CallableStatement st; - if (Util.use42Wrapper()) { + // Make sure SQLServerCallableStatement42 is used for 4.2 and above + if (Util.use42Wrapper() || Util.use43Wrapper()) { st = new SQLServerCallableStatement42(this, sql, nType, nConcur, stmtColEncSetiing); } else { @@ -5248,6 +5254,16 @@ public T unwrap(Class iface) throws SQLException { return t; } + public void beginRequest() throws SQLFeatureNotSupportedException { + DriverJDBCVersion.checkSupportsJDBC43(); + throw new SQLFeatureNotSupportedException("beginRequest not implemented"); + } + + public void endRequest() throws SQLFeatureNotSupportedException { + DriverJDBCVersion.checkSupportsJDBC43(); + throw new SQLFeatureNotSupportedException("endRequest not implemented"); + } + /** * Replace JDBC syntax parameter markets '?' with SQL Server paramter markers @p1, @p2 etc... * diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java new file mode 100644 index 000000000..217bcfc72 --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java @@ -0,0 +1,36 @@ +package com.microsoft.sqlserver.jdbc; + +import java.sql.SQLFeatureNotSupportedException; +import java.sql.ShardingKey; + +public class SQLServerConnection43 extends SQLServerConnection implements ISQLServerConnection43 { + + SQLServerConnection43(String parentInfo) throws SQLServerException { + super(parentInfo); + } + + public void setShardingKey(ShardingKey shardingKey) throws SQLServerException { + DriverJDBCVersion.checkSupportsJDBC43(); + throw new SQLServerException("setShardingKey not implemented", new SQLFeatureNotSupportedException("setShardingKey not implemented")); + } + + public void setShardingKey(ShardingKey shardingKey, + ShardingKey superShardingKey) throws SQLServerException { + DriverJDBCVersion.checkSupportsJDBC43(); + 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")); + } + + public boolean setShardingKeyIfValid(ShardingKey shardingKey, + ShardingKey superShardingKey, + int timeout) throws SQLServerException { + DriverJDBCVersion.checkSupportsJDBC43(); + throw new SQLServerException("setShardingKeyIfValid not implemented", new SQLFeatureNotSupportedException("setShardingKeyIfValid not implemented")); + } + +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java index 49a6b25ba..ebe4fee35 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java @@ -864,7 +864,7 @@ private void setIntProperty(Properties props, int propValue) { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "set" + propKey, propValue); - props.setProperty(propKey, new Integer(propValue).toString()); + props.setProperty(propKey, Integer.valueOf(propValue).toString()); loggerExternal.exiting(getClassNameLogging(), "set" + propKey); } @@ -1003,7 +1003,13 @@ SQLServerConnection getConnectionInternal(String username, // Create new connection and connect. if (dsLogger.isLoggable(Level.FINER)) dsLogger.finer(toString() + " Begin create new connection."); - SQLServerConnection result = new SQLServerConnection(toString()); + SQLServerConnection result = null; + if (Util.use43Wrapper()) { + result = new SQLServerConnection43(toString()); + } + else { + result = new SQLServerConnection(toString()); + } result.connect(mergedProps, pooledConnection); if (dsLogger.isLoggable(Level.FINER)) dsLogger.finer(toString() + " End create new connection " + result.toString()); @@ -1105,6 +1111,7 @@ public T unwrap(Class iface) throws SQLException { return t; } + // Returns unique id for each DataSource instance. private static int nextDataSourceID() { return baseDataSourceID.incrementAndGet(); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java index 761eb26ae..6518d7057 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java @@ -8,6 +8,7 @@ package com.microsoft.sqlserver.jdbc; +import java.lang.reflect.InvocationTargetException; import java.util.Hashtable; import javax.naming.Context; @@ -59,7 +60,7 @@ public Object getObjectInstance(Object ref, // Create class instance and initialize using reference. Class dataSourceClass = Class.forName(className); - Object dataSourceClassInstance = dataSourceClass.newInstance(); + Object dataSourceClassInstance = dataSourceClass.getDeclaredConstructor().newInstance(); // If this class we created does not cast to SQLServerDataSource, then caller // passed in the wrong reference to our factory. @@ -79,6 +80,18 @@ public Object getObjectInstance(Object ref, catch (IllegalAccessException e) { SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true); } + 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); + } // no chance of getting here but to keep the compiler happy return null; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index d0ac93479..92ab65877 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -144,90 +144,90 @@ private void checkClosed() throws SQLServerException { } } - private final static String ASC_OR_DESC = "ASC_OR_DESC"; - private final static String ATTR_NAME = "ATTR_NAME"; - private final static String ATTR_TYPE_NAME = "ATTR_TYPE_NAME"; - private final static String ATTR_SIZE = "ATTR_SIZE"; - private final static String ATTR_DEF = "ATTR_DEF"; - private final static String BASE_TYPE = "BASE_TYPE"; - private final static String BUFFER_LENGTH = "BUFFER_LENGTH"; - private final static String CARDINALITY = "CARDINALITY"; - private final static String CHAR_OCTET_LENGTH = "CHAR_OCTET_LENGTH"; - private final static String CLASS_NAME = "CLASS_NAME"; - private final static String COLUMN_DEF = "COLUMN_DEF"; - private final static String COLUMN_NAME = "COLUMN_NAME"; - private final static String COLUMN_SIZE = "COLUMN_SIZE"; - private final static String COLUMN_TYPE = "COLUMN_TYPE"; - private final static String DATA_TYPE = "DATA_TYPE"; - private final static String DECIMAL_DIGITS = "DECIMAL_DIGITS"; - private final static String DEFERRABILITY = "DEFERRABILITY"; - private final static String DELETE_RULE = "DELETE_RULE"; - private final static String FILTER_CONDITION = "FILTER_CONDITION"; - private final static String FK_NAME = "FK_NAME"; - private final static String FKCOLUMN_NAME = "FKCOLUMN_NAME"; - private final static String FKTABLE_CAT = "FKTABLE_CAT"; - private final static String FKTABLE_NAME = "FKTABLE_NAME"; - private final static String FKTABLE_SCHEM = "FKTABLE_SCHEM"; - private final static String GRANTEE = "GRANTEE"; - private final static String GRANTOR = "GRANTOR"; - private final static String INDEX_NAME = "INDEX_NAME"; - private final static String INDEX_QUALIFIER = "INDEX_QUALIFIER"; - private final static String IS_GRANTABLE = "IS_GRANTABLE"; - private final static String IS_NULLABLE = "IS_NULLABLE"; - private final static String KEY_SEQ = "KEY_SEQ"; - private final static String LENGTH = "LENGTH"; - private final static String NON_UNIQUE = "NON_UNIQUE"; - private final static String NULLABLE = "NULLABLE"; - private final static String NUM_INPUT_PARAMS = "NUM_INPUT_PARAMS"; - private final static String NUM_OUTPUT_PARAMS = "NUM_OUTPUT_PARAMS"; - private final static String NUM_PREC_RADIX = "NUM_PREC_RADIX"; - private final static String NUM_RESULT_SETS = "NUM_RESULT_SETS"; - private final static String ORDINAL_POSITION = "ORDINAL_POSITION"; - private final static String PAGES = "PAGES"; - private final static String PK_NAME = "PK_NAME"; - private final static String PKCOLUMN_NAME = "PKCOLUMN_NAME"; - private final static String PKTABLE_CAT = "PKTABLE_CAT"; - private final static String PKTABLE_NAME = "PKTABLE_NAME"; - private final static String PKTABLE_SCHEM = "PKTABLE_SCHEM"; - private final static String PRECISION = "PRECISION"; - private final static String PRIVILEGE = "PRIVILEGE"; - private final static String PROCEDURE_CAT = "PROCEDURE_CAT"; - private final static String PROCEDURE_NAME = "PROCEDURE_NAME"; - private final static String PROCEDURE_SCHEM = "PROCEDURE_SCHEM"; - private final static String PROCEDURE_TYPE = "PROCEDURE_TYPE"; - private final static String PSEUDO_COLUMN = "PSEUDO_COLUMN"; - private final static String RADIX = "RADIX"; - private final static String REMARKS = "REMARKS"; - private final static String SCALE = "SCALE"; - private final static String SCOPE = "SCOPE"; - private final static String SCOPE_CATALOG = "SCOPE_CATALOG"; - private final static String SCOPE_SCHEMA = "SCOPE_SCHEMA"; - private final static String SCOPE_TABLE = "SCOPE_TABLE"; - private final static String SOURCE_DATA_TYPE = "SOURCE_DATA_TYPE"; - private final static String SQL_DATA_TYPE = "SQL_DATA_TYPE"; - private final static String SQL_DATETIME_SUB = "SQL_DATETIME_SUB"; - private final static String SS_DATA_TYPE = "SS_DATA_TYPE"; - private final static String SUPERTABLE_NAME = "SUPERTABLE_NAME"; - private final static String SUPERTYPE_CAT = "SUPERTYPE_CAT"; - private final static String SUPERTYPE_NAME = "SUPERTYPE_NAME"; - private final static String SUPERTYPE_SCHEM = "SUPERTYPE_SCHEM"; - private final static String TABLE_CAT = "TABLE_CAT"; - private final static String TABLE_NAME = "TABLE_NAME"; - private final static String TABLE_SCHEM = "TABLE_SCHEM"; - private final static String TABLE_TYPE = "TABLE_TYPE"; - private final static String TYPE = "TYPE"; - private final static String TYPE_CAT = "TYPE_CAT"; - private final static String TYPE_NAME = "TYPE_NAME"; - private final static String TYPE_SCHEM = "TYPE_SCHEM"; - private final static String UPDATE_RULE = "UPDATE_RULE"; - private final static String FUNCTION_CAT = "FUNCTION_CAT"; - private final static String FUNCTION_NAME = "FUNCTION_NAME"; - private final static String FUNCTION_SCHEM = "FUNCTION_SCHEM"; - private final static String FUNCTION_TYPE = "FUNCTION_TYPE"; - private final static String SS_IS_SPARSE = "SS_IS_SPARSE"; - private final static String SS_IS_COLUMN_SET = "SS_IS_COLUMN_SET"; - private final static String SS_IS_COMPUTED = "SS_IS_COMPUTED"; - private final static String IS_AUTOINCREMENT = "IS_AUTOINCREMENT"; + private static final String ASC_OR_DESC = "ASC_OR_DESC"; + private static final String ATTR_NAME = "ATTR_NAME"; + private static final String ATTR_TYPE_NAME = "ATTR_TYPE_NAME"; + private static final String ATTR_SIZE = "ATTR_SIZE"; + private static final String ATTR_DEF = "ATTR_DEF"; + private static final String BASE_TYPE = "BASE_TYPE"; + private static final String BUFFER_LENGTH = "BUFFER_LENGTH"; + private static final String CARDINALITY = "CARDINALITY"; + private static final String CHAR_OCTET_LENGTH = "CHAR_OCTET_LENGTH"; + private static final String CLASS_NAME = "CLASS_NAME"; + private static final String COLUMN_DEF = "COLUMN_DEF"; + private static final String COLUMN_NAME = "COLUMN_NAME"; + private static final String COLUMN_SIZE = "COLUMN_SIZE"; + private static final String COLUMN_TYPE = "COLUMN_TYPE"; + private static final String DATA_TYPE = "DATA_TYPE"; + private static final String DECIMAL_DIGITS = "DECIMAL_DIGITS"; + private static final String DEFERRABILITY = "DEFERRABILITY"; + private static final String DELETE_RULE = "DELETE_RULE"; + private static final String FILTER_CONDITION = "FILTER_CONDITION"; + private static final String FK_NAME = "FK_NAME"; + private static final String FKCOLUMN_NAME = "FKCOLUMN_NAME"; + private static final String FKTABLE_CAT = "FKTABLE_CAT"; + private static final String FKTABLE_NAME = "FKTABLE_NAME"; + private static final String FKTABLE_SCHEM = "FKTABLE_SCHEM"; + private static final String GRANTEE = "GRANTEE"; + private static final String GRANTOR = "GRANTOR"; + private static final String INDEX_NAME = "INDEX_NAME"; + private static final String INDEX_QUALIFIER = "INDEX_QUALIFIER"; + private static final String IS_GRANTABLE = "IS_GRANTABLE"; + private static final String IS_NULLABLE = "IS_NULLABLE"; + private static final String KEY_SEQ = "KEY_SEQ"; + private static final String LENGTH = "LENGTH"; + private static final String NON_UNIQUE = "NON_UNIQUE"; + private static final String NULLABLE = "NULLABLE"; + private static final String NUM_INPUT_PARAMS = "NUM_INPUT_PARAMS"; + private static final String NUM_OUTPUT_PARAMS = "NUM_OUTPUT_PARAMS"; + private static final String NUM_PREC_RADIX = "NUM_PREC_RADIX"; + private static final String NUM_RESULT_SETS = "NUM_RESULT_SETS"; + private static final String ORDINAL_POSITION = "ORDINAL_POSITION"; + private static final String PAGES = "PAGES"; + private static final String PK_NAME = "PK_NAME"; + private static final String PKCOLUMN_NAME = "PKCOLUMN_NAME"; + private static final String PKTABLE_CAT = "PKTABLE_CAT"; + private static final String PKTABLE_NAME = "PKTABLE_NAME"; + private static final String PKTABLE_SCHEM = "PKTABLE_SCHEM"; + private static final String PRECISION = "PRECISION"; + private static final String PRIVILEGE = "PRIVILEGE"; + private static final String PROCEDURE_CAT = "PROCEDURE_CAT"; + private static final String PROCEDURE_NAME = "PROCEDURE_NAME"; + private static final String PROCEDURE_SCHEM = "PROCEDURE_SCHEM"; + private static final String PROCEDURE_TYPE = "PROCEDURE_TYPE"; + private static final String PSEUDO_COLUMN = "PSEUDO_COLUMN"; + private static final String RADIX = "RADIX"; + private static final String REMARKS = "REMARKS"; + private static final String SCALE = "SCALE"; + private static final String SCOPE = "SCOPE"; + private static final String SCOPE_CATALOG = "SCOPE_CATALOG"; + private static final String SCOPE_SCHEMA = "SCOPE_SCHEMA"; + private static final String SCOPE_TABLE = "SCOPE_TABLE"; + private static final String SOURCE_DATA_TYPE = "SOURCE_DATA_TYPE"; + private static final String SQL_DATA_TYPE = "SQL_DATA_TYPE"; + private static final String SQL_DATETIME_SUB = "SQL_DATETIME_SUB"; + private static final String SS_DATA_TYPE = "SS_DATA_TYPE"; + private static final String SUPERTABLE_NAME = "SUPERTABLE_NAME"; + private static final String SUPERTYPE_CAT = "SUPERTYPE_CAT"; + private static final String SUPERTYPE_NAME = "SUPERTYPE_NAME"; + private static final String SUPERTYPE_SCHEM = "SUPERTYPE_SCHEM"; + private static final String TABLE_CAT = "TABLE_CAT"; + private static final String TABLE_NAME = "TABLE_NAME"; + private static final String TABLE_SCHEM = "TABLE_SCHEM"; + private static final String TABLE_TYPE = "TABLE_TYPE"; + private static final String TYPE = "TYPE"; + private static final String TYPE_CAT = "TYPE_CAT"; + private static final String TYPE_NAME = "TYPE_NAME"; + private static final String TYPE_SCHEM = "TYPE_SCHEM"; + private static final String UPDATE_RULE = "UPDATE_RULE"; + private static final String FUNCTION_CAT = "FUNCTION_CAT"; + private static final String FUNCTION_NAME = "FUNCTION_NAME"; + private static final String FUNCTION_SCHEM = "FUNCTION_SCHEM"; + 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_AUTOINCREMENT = "IS_AUTOINCREMENT"; /** * Make a simple query execute and return the result from it. This is to be used only for internal queries without any user input. @@ -401,6 +401,13 @@ public boolean supportsRefCursors() throws SQLException { return false; } + public boolean supportsSharding() throws SQLException { + DriverJDBCVersion.checkSupportsJDBC43(); + checkClosed(); + + return false; + } + /* L0 */ public java.sql.ResultSet getCatalogs() throws SQLServerException { if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); @@ -421,7 +428,7 @@ public boolean supportsRefCursors() throws SQLException { return "database"; } - private final static String[] getColumnPrivilegesColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ COLUMN_NAME, + 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, @@ -447,7 +454,7 @@ public boolean supportsRefCursors() throws SQLException { return getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_COLUMN_PRIVILEGES, arguments, getColumnPrivilegesColumnNames); } - private final static String[] getTablesColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ TABLE_TYPE, + 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, @@ -547,14 +554,14 @@ private static String EscapeIDName(String inID) throws SQLServerException { return outID.toString(); } - private final static String[] getColumnsColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ COLUMN_NAME, + private static final String[] getColumnsColumnNames = {/* 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}; // 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 final static String[] getColumnsColumnNamesKatmai = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ COLUMN_NAME, + 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, @@ -612,7 +619,7 @@ private static String EscapeIDName(String inID) throws SQLServerException { return rs; } - private final static String[] getFunctionsColumnNames = {/* 1 */ FUNCTION_CAT, /* 2 */ FUNCTION_SCHEM, /* 3 */ FUNCTION_NAME, + 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}; public java.sql.ResultSet getFunctions(String catalog, @@ -638,7 +645,7 @@ public java.sql.ResultSet getFunctions(String catalog, return getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_STORED_PROCEDURES, arguments, getFunctionsColumnNames); } - private final static String[] getFunctionsColumnsColumnNames = {/* 1 */ FUNCTION_CAT, /* 2 */ FUNCTION_SCHEM, /* 3 */ FUNCTION_NAME, + private static final String[] getFunctionsColumnsColumnNames = {/* 1 */ FUNCTION_CAT, /* 2 */ FUNCTION_SCHEM, /* 3 */ FUNCTION_NAME, /* 4 */ COLUMN_NAME, /* 5 */ COLUMN_TYPE, /* 6 */ DATA_TYPE, /* 7 */ TYPE_NAME, /* 8 */ PRECISION, /* 9 */ LENGTH, /* 10 */ SCALE, /* 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}; @@ -695,7 +702,7 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { /* 4 */ " cast(NULL as char(1)) as DESCRIPTION " + " where 0 = 1"); } - private final static String[] getBestRowIdentifierColumnNames = {/* 1 */ SCOPE, /* 2 */ COLUMN_NAME, /* 3 */ DATA_TYPE, /* 4 */ TYPE_NAME, + 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, @@ -735,7 +742,7 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { return rs; } - private final static String[] pkfkColumnNames = {/* 1 */ PKTABLE_CAT, /* 2 */ PKTABLE_SCHEM, /* 3 */ PKTABLE_NAME, /* 4 */ PKCOLUMN_NAME, + private static final String[] pkfkColumnNames = {/* 1 */ PKTABLE_CAT, /* 2 */ PKTABLE_SCHEM, /* 3 */ PKTABLE_NAME, /* 4 */ PKCOLUMN_NAME, /* 5 */ FKTABLE_CAT, /* 6 */ FKTABLE_SCHEM, /* 7 */ FKTABLE_NAME, /* 8 */ FKCOLUMN_NAME, /* 9 */ KEY_SEQ, /* 10 */ UPDATE_RULE, /* 11 */ DELETE_RULE, /* 12 */ FK_NAME, /* 13 */ PK_NAME, /* 14 */ DEFERRABILITY}; @@ -1005,7 +1012,7 @@ private ResultSet getResultSetForForeignKeyInformation(SQLServerResultSet fkeysR + foreign_keys_combined_tableName + " order by FKTABLE_QUALIFIER, FKTABLE_OWNER, FKTABLE_NAME, KEY_SEQ"); } - private final static String[] getIndexInfoColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ NON_UNIQUE, + private static final String[] getIndexInfoColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ NON_UNIQUE, /* 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}; @@ -1159,7 +1166,7 @@ private ResultSet getResultSetForForeignKeyInformation(SQLServerResultSet fkeysR 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"; } - private final static String[] getPrimaryKeysColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ COLUMN_NAME, + 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, @@ -1179,7 +1186,7 @@ private ResultSet getResultSetForForeignKeyInformation(SQLServerResultSet fkeysR return getResultSetWithProvidedColumnNames(cat, CallableHandles.SP_PKEYS, arguments, getPrimaryKeysColumnNames); } - private final static String[] getProcedureColumnsColumnNames = {/* 1 */ PROCEDURE_CAT, /* 2 */ PROCEDURE_SCHEM, /* 3 */ PROCEDURE_NAME, + private static final String[] getProcedureColumnsColumnNames = {/* 1 */ PROCEDURE_CAT, /* 2 */ PROCEDURE_SCHEM, /* 3 */ PROCEDURE_NAME, /* 4 */ COLUMN_NAME, /* 5 */ COLUMN_TYPE, /* 6 */ DATA_TYPE, /* 7 */ TYPE_NAME, /* 8 */ PRECISION, /* 9 */ LENGTH, /* 10 */ SCALE, /* 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}; @@ -1224,7 +1231,7 @@ private ResultSet getResultSetForForeignKeyInformation(SQLServerResultSet fkeysR return rs; } - private final static String[] getProceduresColumnNames = {/* 1 */ PROCEDURE_CAT, /* 2 */ PROCEDURE_SCHEM, /* 3 */ PROCEDURE_NAME, + 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, @@ -1389,7 +1396,7 @@ public java.sql.ResultSet getSchemas(String catalog, return "DATABASE,IFNULL,USER"; // The functions no reinstated after the CTS certification. } - private final static String[] getTablePrivilegesColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ GRANTOR, + 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, @@ -1539,7 +1546,7 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { return result; } - private final static String[] getVersionColumnsColumnNames = {/* 1 */ SCOPE, /* 2 */ COLUMN_NAME, /* 3 */ DATA_TYPE, /* 4 */ TYPE_NAME, + 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, @@ -1972,10 +1979,7 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { case ResultSet.TYPE_SCROLL_INSENSITIVE: // case SQLServerResultSet.TYPE_SS_SCROLL_STATIC: sensitive synonym case SQLServerResultSet.TYPE_SS_DIRECT_FORWARD_ONLY: - if (ResultSet.CONCUR_READ_ONLY == concurrency) - return true; - else - return false; + return (ResultSet.CONCUR_READ_ONLY == concurrency); } // per spec if we do not know we do not support. return false; @@ -1984,60 +1988,48 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { /* L0 */ public boolean ownUpdatesAreVisible(int type) throws SQLServerException { checkClosed(); checkResultType(type); - if (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type + return (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type || SQLServerResultSet.TYPE_SCROLL_SENSITIVE == type || SQLServerResultSet.TYPE_SS_SCROLL_KEYSET == type - || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type) - return true; - return false; + || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type); } /* L0 */ public boolean ownDeletesAreVisible(int type) throws SQLServerException { checkClosed(); checkResultType(type); - if (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type + return (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type || SQLServerResultSet.TYPE_SCROLL_SENSITIVE == type || SQLServerResultSet.TYPE_SS_SCROLL_KEYSET == type - || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type) - return true; - return false; + || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type); } /* L0 */ public boolean ownInsertsAreVisible(int type) throws SQLServerException { checkClosed(); checkResultType(type); - if (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type + return (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type || SQLServerResultSet.TYPE_SCROLL_SENSITIVE == type || SQLServerResultSet.TYPE_SS_SCROLL_KEYSET == type - || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type) - return true; - return false; + || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type); } /* L0 */ public boolean othersUpdatesAreVisible(int type) throws SQLServerException { checkClosed(); checkResultType(type); - if (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type + return (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type || SQLServerResultSet.TYPE_SCROLL_SENSITIVE == type || SQLServerResultSet.TYPE_SS_SCROLL_KEYSET == type - || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type) - return true; - return false; + || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type); } /* L0 */ public boolean othersDeletesAreVisible(int type) throws SQLServerException { checkClosed(); checkResultType(type); - if (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type + return (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type || SQLServerResultSet.TYPE_SCROLL_SENSITIVE == type || SQLServerResultSet.TYPE_SS_SCROLL_KEYSET == type - || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type) - return true; - return false; + || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type); } /* L0 */ public boolean othersInsertsAreVisible(int type) throws SQLServerException { checkClosed(); checkResultType(type); - if (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type - || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type) - return true; - return false; + 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 { @@ -2049,10 +2041,7 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { /* L0 */ public boolean deletesAreDetected(int type) throws SQLServerException { checkClosed(); checkResultType(type); - if (SQLServerResultSet.TYPE_SS_SCROLL_KEYSET == type) - return true; - else - return false; + return (SQLServerResultSet.TYPE_SS_SCROLL_KEYSET == type); } // Check the result types to make sure the user does not pass a bad value. @@ -2296,14 +2285,14 @@ public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException { // Filter to convert DATA_TYPE column values from the ODBC types // returned by SQL Server to their equivalent JDBC types. final class DataTypeFilter extends IntColumnFilter { - private final static int ODBC_SQL_GUID = -11; - private final static int ODBC_SQL_WCHAR = -8; - private final static int ODBC_SQL_WVARCHAR = -9; - private final static int ODBC_SQL_WLONGVARCHAR = -10; - private final static int ODBC_SQL_FLOAT = 6; - private final static int ODBC_SQL_TIME = -154; - private final static int ODBC_SQL_XML = -152; - private final static int ODBC_SQL_UDT = -151; + private static final int ODBC_SQL_GUID = -11; + private static final int ODBC_SQL_WCHAR = -8; + private static final int ODBC_SQL_WVARCHAR = -9; + private static final int ODBC_SQL_WLONGVARCHAR = -10; + private static final int ODBC_SQL_FLOAT = 6; + private static final int ODBC_SQL_TIME = -154; + private static final int ODBC_SQL_XML = -152; + private static final int ODBC_SQL_UDT = -151; int oneValueToAnother(int odbcType) { switch (odbcType) { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java index 1dfeae276..ae4e1fd09 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java @@ -347,7 +347,7 @@ enum SQLServerDriverBooleanProperty MULTI_SUBNET_FAILOVER ("multiSubnetFailover", false), SERVER_NAME_AS_ACE ("serverNameAsACE", false), SEND_STRING_PARAMETERS_AS_UNICODE ("sendStringParametersAsUnicode", true), - SEND_TIME_AS_DATETIME ("sendTimeAsDatetime", true), + SEND_TIME_AS_DATETIME ("sendTimeAsDatetime", false), TRANSPARENT_NETWORK_IP_RESOLUTION ("TransparentNetworkIPResolution", true), TRUST_SERVER_CERTIFICATE ("trustServerCertificate", false), XOPEN_STATES ("xopenStates", false), @@ -474,7 +474,9 @@ String getClassNameLogging() { java.sql.DriverManager.registerDriver(new SQLServerDriver()); } catch (SQLException e) { - e.printStackTrace(); + if (drLogger.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { + drLogger.finer("Error registering driver: " + e); + } } } @@ -612,7 +614,12 @@ static String getPropertyOnlyName(String name, // Merge connectProperties (from URL) and supplied properties from user. Properties connectProperties = parseAndMergeProperties(Url, suppliedProperties); if (connectProperties != null) { - result = new SQLServerConnection(toString()); + if (Util.use43Wrapper()) { + result = new SQLServerConnection43(toString()); + } + else { + result = new SQLServerConnection(toString()); + } result.connect(connectProperties, null); } loggerExternal.exiting(getClassNameLogging(), "connect", result); @@ -632,7 +639,7 @@ private Properties parseAndMergeProperties(String Url, // put the user properties into the connect properties int nTimeout = DriverManager.getLoginTimeout(); if (nTimeout > 0) { - connectProperties.put(SQLServerDriverIntProperty.LOGIN_TIMEOUT.toString(), new Integer(nTimeout).toString()); + connectProperties.put(SQLServerDriverIntProperty.LOGIN_TIMEOUT.toString(), Integer.valueOf(nTimeout).toString()); } // Merge connectProperties (from URL) and supplied properties from user. diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java index 0d078cc1a..1950daf62 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java @@ -24,6 +24,10 @@ final class DriverJDBCVersion { static final void checkSupportsJDBC42() { } + + static final void checkSupportsJDBC43() { + throw new UnsupportedOperationException(SQLServerException.getErrString("R_notSupported")); + } static final void throwBatchUpdateException(SQLServerException lastError, long[] updateCounts) throws BatchUpdateException { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java similarity index 55% rename from src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java rename to src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java index 09bb912bf..1f60cd53a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java @@ -8,24 +8,29 @@ package com.microsoft.sqlserver.jdbc; +import java.sql.BatchUpdateException; + /** - * Shims for JDBC 4.1 JAR. + * Shims for JDBC 4.3 JAR. * - * JDBC 4.1 public methods should always check the SQLServerJdbcVersion first to make sure that they are not operable in any earlier driver version. + * JDBC 4.3 public methods should always check the SQLServerJdbcVersion first to make sure that they are not operable in any earlier driver version. * That is, they should throw an exception, be a no-op, or whatever. */ final class DriverJDBCVersion { + // The 4.3 driver is compliant to JDBC 4.3. static final int major = 4; - static final int minor = 1; + static final int minor = 3; + static final void checkSupportsJDBC43() { + } + static final void checkSupportsJDBC42() { - throw new UnsupportedOperationException(SQLServerException.getErrString("R_notSupported")); } - // Stub for the new overloaded method in BatchUpdateException in JDBC 4.2 static final void throwBatchUpdateException(SQLServerException lastError, - long[] updateCounts) { - throw new UnsupportedOperationException(SQLServerException.getErrString("R_notSupported")); + long[] updateCounts) throws BatchUpdateException { + throw new BatchUpdateException(lastError.getMessage(), lastError.getSQLState(), lastError.getErrorCode(), updateCounts, + new Throwable(lastError.getMessage())); } -} +} \ No newline at end of file diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 63e701505..dc21fbbdb 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -339,7 +339,6 @@ protected Object[][] getContents() { {"R_TVPEmptyMetadata", "There are not enough fields in the Structured type. Structured types must have at least one field."}, {"R_TVPInvalidValue", "The value provided for Table-Valued Parameter {0} is not valid. Only SQLServerDataTable, ResultSet and ISQLServerDataRecord objects are supported."}, {"R_TVPInvalidColumnValue", "Input data is not in correct format."}, - {"R_AADIntegratedOnNonWindows","ActiveDirectoryIntegrated is only supported on Windows operating systems."}, {"R_TVPSortOrdinalGreaterThanFieldCount", "The sort ordinal {0} on field {1} exceeds the total number of fields."}, {"R_TVPMissingSortOrderOrOrdinal", "The sort order and ordinal must either both be specified, or neither should be specified (SortOrder.Unspecified and -1). The values given were: order = {0}, ordinal = {1}."}, {"R_TVPDuplicateSortOrdinal", "The sort ordinal {0} was specified twice."}, @@ -390,7 +389,7 @@ protected Object[][] getContents() { {"R_invalidStringValue", "SQL_VARIANT does not support string values of length greater than 8000."}, {"R_invalidValueForTVPWithSQLVariant", "Use of TVPs containing null sql_variant columns is not supported."}, {"R_invalidDataTypeSupportForSQLVariant", "Unexpected TDS type ' '{0}' ' in SQL_VARIANT."}, - {"R_sslProtocolPropertyDescription", "SSL protocol label from TLS, TLSv1, TLSv1.1 & TLSv1.2. The default is TLS."}, - {"R_invalidSSLProtocol", "SSL Protocol {0} label is not valid. Only TLS, TLSv1, TLSv1.1 & TLSv1.2 are supported."}, + {"R_sslProtocolPropertyDescription", "SSL protocol label from TLS, TLSv1, TLSv1.1, and TLSv1.2. The default is TLS."}, + {"R_invalidSSLProtocol", "SSL Protocol {0} label is not valid. Only TLS, TLSv1, TLSv1.1, and TLSv1.2 are supported."}, }; } \ No newline at end of file diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java index 9b7933705..37e1ac033 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java @@ -939,7 +939,6 @@ private void updateCurrentRow(int rowsToMove) { * and so on. * * @return false when there are no more rows to read - * @return true otherwise */ public boolean next() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "next"); @@ -1041,8 +1040,7 @@ public boolean wasNull() throws SQLServerException { } /** - * @return true if the cursor is before the first row in this result set - * @return false otherwise or if thie result set contains no rows. + * @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 { loggerExternal.entering(getClassNameLogging(), "isBeforeFirst"); @@ -1144,7 +1142,6 @@ public boolean isAfterLast() throws SQLServerException { * TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC. * * @return true if the cursor is on the first row in this result set - * @return false otherwise */ public boolean isFirst() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "isFirst"); @@ -1182,7 +1179,6 @@ public boolean isFirst() throws SQLServerException { * 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 - * @return false otherwise */ public boolean isLast() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "isLast"); @@ -1302,8 +1298,7 @@ private void moveAfterLast() throws SQLServerException { * 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 - * @return false if there are no rows in this ResultSet object + * @return true if the cursor is on a valid row, otherwise returns false if there are no rows in this ResultSet object */ public boolean first() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "first"); @@ -1353,8 +1348,7 @@ private void moveFirst() throws SQLServerException { * 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 - * @return false if there are no rows in this ResultSet object + * @return true if the cursor is on a valid row, otherwise returns false if there are no rows in this ResultSet object */ public boolean last() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "last"); @@ -1442,8 +1436,8 @@ public int getRow() throws SQLServerException { * 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 false if the cursor is before the first row or after the last row + * @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 */ public boolean absolute(int row) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "absolute"); @@ -1759,7 +1753,6 @@ private int clientMoveAbsolute(int row) throws SQLServerException { * 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 false otherwise */ public boolean previous() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "previous"); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSetMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSetMetaData.java index 693f3fe0c..93de065ee 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSetMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSetMetaData.java @@ -21,7 +21,6 @@ public final class SQLServerResultSetMetaData implements java.sql.ResultSetMetaData { private SQLServerConnection con; private final SQLServerResultSet rs; - public int nBeforeExecuteCols; static final private java.util.logging.Logger logger = java.util.logging.Logger .getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerResultSetMetaData"); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSQLXML.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSQLXML.java index 7e8881d8b..3e99ab41f 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSQLXML.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSQLXML.java @@ -23,6 +23,8 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; @@ -48,8 +50,6 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; -import org.xml.sax.helpers.XMLReaderFactory; - /** * SQLServerSQLXML represents an XML object and implements a java.sql.SQLXML. */ @@ -405,12 +405,14 @@ private DOMSource getDOMSource() throws SQLException { private SAXSource getSAXSource() throws SQLException { try { InputSource src = new InputSource(contents); - XMLReader reader = XMLReaderFactory.createXMLReader(); + SAXParserFactory factory = SAXParserFactory.newInstance(); + SAXParser parser = factory.newSAXParser(); + XMLReader reader = parser.getXMLReader(); SAXSource saxSource = new SAXSource(reader, src); return saxSource; } - catch (SAXException e) { + catch (SAXException | ParserConfigurationException e) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_failedToParseXML")); Object[] msgArgs = {e.toString()}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index 9385fd165..696cfa6c5 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -1569,7 +1569,8 @@ boolean onInfo(TDSReader tdsReader) throws SQLServerException { // Not an error. Is it a result set? else if (nextResult.isResultSet()) { - if (Util.use42Wrapper()) { + // Make sure SQLServerResultSet42 is used for 4.2 and above + if (Util.use42Wrapper() || Util.use43Wrapper()) { resultSet = new SQLServerResultSet42(this); } else { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java index 76b886786..04f9335ff 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java @@ -18,7 +18,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; -import javax.xml.bind.DatatypeConverter; +import java.util.Base64;; class CacheClear implements Runnable { @@ -98,7 +98,7 @@ SQLServerSymmetricKey getKey(EncryptionKeyInfo keyInfo, String keyLookupValue; keyLookupValuebuffer.append(":"); - keyLookupValuebuffer.append(DatatypeConverter.printBase64Binary((new String(keyInfo.encryptedKey, UTF_8)).getBytes())); + keyLookupValuebuffer.append(Base64.getEncoder().encodeToString((new String(keyInfo.encryptedKey, UTF_8)).getBytes())); keyLookupValuebuffer.append(":"); keyLookupValuebuffer.append(keyInfo.keyStoreName); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAConnection.java index c1f8d69fd..424fe52ce 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAConnection.java @@ -44,7 +44,13 @@ public final class SQLServerXAConnection extends SQLServerPooledConnection imple if (xaLogger.isLoggable(Level.FINER)) xaLogger.finer("Creating an internal control connection for" + toString()); - physicalControlConnection = new SQLServerConnection(toString()); + physicalControlConnection = null; + if (Util.use43Wrapper()) { + physicalControlConnection = new SQLServerConnection43(toString()); + } + else { + physicalControlConnection = new SQLServerConnection(toString()); + } physicalControlConnection.connect(controlConnectionProperties, null); if (xaLogger.isLoggable(Level.FINER)) xaLogger.finer("Created an internal control connection" + physicalControlConnection.toString() + " for " + toString() diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAResource.java index 8e72d0f35..53ff357d4 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAResource.java @@ -16,11 +16,9 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Properties; -import java.util.Vector; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; - import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; @@ -165,7 +163,10 @@ public final class SQLServerXAResource implements javax.transaction.xa.XAResourc public static final int SSTRANSTIGHTLYCPLD = 0x8000; private SQLServerCallableStatement[] xaStatements = {null, null, null, null, null, null, null, null, null, null}; private final String traceID; - + /** + * Variable that shows how many times we attempt the recovery, e.g in case of MSDTC restart + */ + private int recoveryAttempt = 0; static { xaInitLock = new Object(); } @@ -381,7 +382,7 @@ private String typeDisplay(int type) { SQLServerCallableStatement cs = null; try { synchronized (this) { - if (!xaInitDone) { + if (!xaInitDone) { try { synchronized (xaInitLock) { SQLServerCallableStatement initCS = null; @@ -634,6 +635,14 @@ else if (-1 != version.indexOf('.')) { } } } + if (XA_RECOVER == nType && XA_OK != nStatus && recoveryAttempt < 1) { + // if recover failed, attempt to start again - adding the variable to check to attempt only once otherwise throw exception that recovery fails + // this is added since before this change, if we restart the MSDTC and attempt to do recovery, driver will throw exception + //"The function RECOVER: failed. The status is: -3" + recoveryAttempt++; + DTC_XA_Interface(XA_START, xid, TMNOFLAGS); + return DTC_XA_Interface(XA_RECOVER, xid, xaFlags); + } // prepare and end can return XA_RDONLY // Think should we just check for nStatus to be greater than or equal to zero instead of this check if (((XA_RDONLY == nStatus) && (XA_END != nType && XA_PREPARE != nType)) || (XA_OK != nStatus && XA_RDONLY != nStatus)) { @@ -658,7 +667,6 @@ else if (-1 != version.indexOf('.')) { xaLogger.finer(toString() + " Ignoring exception:" + e1); } } - throw e; } else { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SqlVariant.java b/src/main/java/com/microsoft/sqlserver/jdbc/SqlVariant.java index 2d4c13436..868db7102 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SqlVariant.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SqlVariant.java @@ -62,7 +62,7 @@ static sqlVariantProbBytes valueOf(int intValue) { if (!(0 <= intValue && intValue < valuesTypes.length) || null == (tdsType = valuesTypes[intValue])) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unknownSSType")); - Object[] msgArgs = {new Integer(intValue)}; + Object[] msgArgs = {Integer.valueOf(intValue)}; throw new IllegalArgumentException(form.format(msgArgs)); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java index c1d6d81dc..c8842ae09 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java @@ -391,7 +391,8 @@ else if (ch == ':') if (null != name) { if (logger.isLoggable(Level.FINE)) { if (false == name.equals(SQLServerDriverStringProperty.USER.toString())) { - if (!name.toLowerCase(Locale.ENGLISH).contains("password")) { + if (!name.toLowerCase(Locale.ENGLISH).contains("password") && + !name.toLowerCase(Locale.ENGLISH).contains("keystoresecret")) { logger.fine("Property:" + name + " Value:" + value); } else { @@ -785,10 +786,7 @@ static final String readGUID(byte[] inputGUID) throws SQLServerException { static boolean IsActivityTraceOn() { LogManager lm = LogManager.getLogManager(); String activityTrace = lm.getProperty(ActivityIdTraceProperty); - if ("on".equalsIgnoreCase(activityTrace)) - return true; - else - return false; + return ("on".equalsIgnoreCase(activityTrace)); } /** @@ -1008,6 +1006,28 @@ static synchronized boolean checkIfNeedNewAccessToken(SQLServerConnection connec static boolean use42Wrapper() { return use42Wrapper; } + + static final boolean use43Wrapper; + + static { + boolean supportJDBC43 = true; + try { + DriverJDBCVersion.checkSupportsJDBC43(); + } + catch (UnsupportedOperationException e) { + supportJDBC43 = false; + } + + double jvmVersion = Double.parseDouble(Util.SYSTEM_SPEC_VERSION); + + use43Wrapper = supportJDBC43 && (9 <= jvmVersion); + } + + // if driver is for JDBC 43 and jvm version is 9 or higher, then always return as SQLServerConnection43, + // otherwise return SQLServerConnection + static boolean use43Wrapper() { + return use43Wrapper; + } } final class SQLIdentifier { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java index e0ea30378..584cfd681 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java @@ -2208,7 +2208,7 @@ void execute(DTV dtv, if (null != bigDecimalValue) { Integer inScale = dtv.getScale(); if (null != inScale && inScale != bigDecimalValue.scale()) - bigDecimalValue = bigDecimalValue.setScale(inScale, BigDecimal.ROUND_DOWN); + bigDecimalValue = bigDecimalValue.setScale(inScale, RoundingMode.DOWN); } dtv.setValue(bigDecimalValue, JavaType.BIGDECIMAL); diff --git a/src/main/java/mssql/googlecode/concurrentlinkedhashmap/ConcurrentLinkedHashMap.java b/src/main/java/mssql/googlecode/concurrentlinkedhashmap/ConcurrentLinkedHashMap.java index a52c70e7b..10f892497 100644 --- a/src/main/java/mssql/googlecode/concurrentlinkedhashmap/ConcurrentLinkedHashMap.java +++ b/src/main/java/mssql/googlecode/concurrentlinkedhashmap/ConcurrentLinkedHashMap.java @@ -1446,9 +1446,9 @@ Object readResolve() { * provides a flexible approach for constructing customized instances with * a named parameter syntax. It can be used in the following manner: *
{@code
-   * ConcurrentMap> graph = new Builder>()
+   * ConcurrentMap> graph = new Builder>()
    *     .maximumWeightedCapacity(5000)
-   *     .weigher(Weighers.set())
+   *     .weigher(Weighers.set())
    *     .build();
    * }
*/ diff --git a/src/samples/alwaysencrypted/src/main/java/AlwaysEncrypted.java b/src/samples/alwaysencrypted/src/main/java/AlwaysEncrypted.java index a337f0d74..bf91979c1 100644 --- a/src/samples/alwaysencrypted/src/main/java/AlwaysEncrypted.java +++ b/src/samples/alwaysencrypted/src/main/java/AlwaysEncrypted.java @@ -14,7 +14,6 @@ import java.sql.SQLException; import java.sql.Statement; -import javax.xml.bind.DatatypeConverter; import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionJavaKeyStoreProvider; import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionKeyStoreProvider; @@ -116,7 +115,7 @@ public static void main(String[] args) { */ String createCEKSQL = "CREATE COLUMN ENCRYPTION KEY " + columnEncryptionKey + " WITH VALUES ( " + " COLUMN_MASTER_KEY = " + columnMasterKeyName + " , ALGORITHM = '" + algorithm + "' , ENCRYPTED_VALUE = 0x" - + DatatypeConverter.printHexBinary(encryptedCEK) + " ) "; + + bytesToHexString(encryptedCEK, encryptedCEK.length) + " ) "; try (Statement cekStatement = sourceConnection.createStatement()) { cekStatement.executeUpdate(createCEKSQL); @@ -129,6 +128,27 @@ public static void main(String[] args) { e.printStackTrace(); } } + + /** + * + * @param b + * byte value + * @param length + * length of the array + * @return + */ + final static char[] hexChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + private static String bytesToHexString(byte[] b, + int length) { + StringBuilder sb = new StringBuilder(length * 2); + for (int i = 0; i < length; i++) { + int hexVal = b[i] & 0xFF; + sb.append(hexChars[(hexVal & 0xF0) >> 4]); + sb.append(hexChars[(hexVal & 0x0F)]); + } + return sb.toString(); + } // To avoid storing the sourceConnection String in your code, // you can retrieve it from a configuration file. diff --git a/src/samples/resultsets/src/main/java/retrieveRS.java b/src/samples/resultsets/src/main/java/retrieveRS.java index 0a4bcc5f4..2422d099f 100644 --- a/src/samples/resultsets/src/main/java/retrieveRS.java +++ b/src/samples/resultsets/src/main/java/retrieveRS.java @@ -104,7 +104,7 @@ private static void createTable(Connection con) throws SQLException { stmt.execute(sql); - sql = "INSERT Product_JDBC_Sample VALUES ('Adjustable Race','AR-5381','0','0',NULL,'1000','750','0.00','0.00',NULL,NULL,NULL,NULL,'0',NULL,NULL,NULL,NULL,NULL,'2008-04-30 00:00:00.000',NULL,NULL,'694215B7-08F7-4C0D-ACB1-D734BA44C0C8','2014-02-08 10:01:36.827') "; + sql = "INSERT Product_JDBC_Sample VALUES ('Adjustable Time','AR-5381','0','0',NULL,'1000','750','0.00','0.00',NULL,NULL,NULL,NULL,'0',NULL,NULL,NULL,NULL,NULL,'2008-04-30 00:00:00.000',NULL,NULL,'694215B7-08F7-4C0D-ACB1-D734BA44C0C8','2014-02-08 10:01:36.827') "; stmt.execute(sql); sql = "INSERT Product_JDBC_Sample VALUES ('ML Bottom Bracket','BB-8107','0','0',NULL,'1000','750','0.00','0.00',NULL,NULL,NULL,NULL,'0',NULL,NULL,NULL,NULL,NULL,'2008-04-30 00:00:00.000',NULL,NULL,'694215B7-08F7-4C0D-ACB1-D734BA44C0C8','2014-02-08 10:01:36.827') "; 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 9ae2445c1..dd4d588df 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java @@ -21,7 +21,6 @@ import java.util.LinkedList; import java.util.Properties; -import javax.xml.bind.DatatypeConverter; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -712,7 +711,7 @@ private static void createCEK(SQLServerColumnEncryptionKeyStoreProvider storePro String cekSql = null; byte[] key = storeProvider.encryptColumnEncryptionKey(javaKeyAliases, "RSA_OAEP", valuesDefault); cekSql = "CREATE COLUMN ENCRYPTION KEY " + cekName + " WITH VALUES " + "(COLUMN_MASTER_KEY = " + cmkName - + ", ALGORITHM = 'RSA_OAEP', ENCRYPTED_VALUE = 0x" + DatatypeConverter.printHexBinary(key) + ")" + ";"; + + ", ALGORITHM = 'RSA_OAEP', ENCRYPTED_VALUE = 0x" + Util.bytesToHexString(key, key.length) + ")" + ";"; stmt.execute(cekSql); } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java b/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java new file mode 100644 index 000000000..6873bbffe --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java @@ -0,0 +1,228 @@ +/* + * 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.Connection; +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.JDBCType; +import java.sql.SQLException; +import java.sql.ShardingKey; +import java.util.Enumeration; +import java.util.stream.Stream; + +import javax.sql.ConnectionPoolDataSource; +import javax.sql.PooledConnection; +import javax.sql.XAConnection; + +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.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.util.Util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * Tests JDBC 4.3 APIs + * + */ +@RunWith(JUnitPlatform.class) +public class JDBC43Test extends AbstractTest { + ShardingKey superShardingKey = null; + ShardingKey shardingKey = null; + + /** + * Tests that we are throwing the unsupported exception for connectionBuilder() + * @throws SQLException + * @throws TestAbortedException + * + * @since 1.9 + */ + @Test + public void connectionBuilderTest() throws TestAbortedException, SQLException { + assumeTrue(Util.supportJDBC43(connection)); + SQLServerDataSource ds = new SQLServerDataSource(); + try { + superShardingKey = ds.createShardingKeyBuilder().subkey("EASTERN_REGION", JDBCType.VARCHAR).build(); + } + catch (SQLException e) { + assert (e.getMessage().contains("not implemented")); + } + + try { + shardingKey = ds.createShardingKeyBuilder().subkey("PITTSBURGH_BRANCH", JDBCType.VARCHAR).build(); + } + catch (SQLException e) { + assert (e.getMessage().contains("not implemented")); + } + + try { + Connection con = ds.createConnectionBuilder().user("rafa").password("tennis").shardingKey(shardingKey).superShardingKey(superShardingKey) + .build(); + } + catch (SQLException e) { + assert (e.getMessage().contains("not implemented")); + } + } + + /** + * Tests that we are throwing the unsupported exception for connectionBuilder() + * @throws SQLException + * @throws TestAbortedException + * + * @since 1.9 + */ + @Test + public void xaConnectionBuilderTest() throws TestAbortedException, SQLException { + assumeTrue(Util.supportJDBC43(connection)); + SQLServerXADataSource ds = new SQLServerXADataSource(); + try { + superShardingKey = ds.createShardingKeyBuilder().subkey("EASTERN_REGION", JDBCType.VARCHAR).build(); + } + catch (SQLException e) { + assert (e.getMessage().contains("not implemented")); + } + + try { + shardingKey = ds.createShardingKeyBuilder().subkey("PITTSBURGH_BRANCH", JDBCType.VARCHAR).build(); + } + catch (SQLException e) { + assert (e.getMessage().contains("not implemented")); + } + + try { + XAConnection con = ds.createXAConnectionBuilder().user("rafa").password("tennis").shardingKey(shardingKey) + .superShardingKey(superShardingKey).build(); + } + catch (SQLException e) { + assert (e.getMessage().contains("not implemented")); + } + } + + /** + * Tests that we are throwing the unsupported exception for createPooledConnectionBuilder() + * @throws SQLException + * @throws TestAbortedException + * @since 1.9 + */ + @Test + public void connectionPoolDataSourceTest() throws TestAbortedException, SQLException { + assumeTrue(Util.supportJDBC43(connection)); + ConnectionPoolDataSource ds = new SQLServerConnectionPoolDataSource(); + try { + superShardingKey = ds.createShardingKeyBuilder().subkey("EASTERN_REGION", JDBCType.VARCHAR).build(); + } + catch (SQLException e) { + assert (e.getMessage().contains("not implemented")); + } + + try { + shardingKey = ds.createShardingKeyBuilder().subkey("PITTSBURGH_BRANCH", JDBCType.VARCHAR).build(); + } + catch (SQLException e) { + assert (e.getMessage().contains("not implemented")); + } + try { + PooledConnection con = ds.createPooledConnectionBuilder().user("rafa").password("tennis").shardingKey(shardingKey) + .superShardingKey(superShardingKey).build(); + } + catch (SQLException e) { + assert (e.getMessage().contains("not implemented")); + } + } + + /** + * Tests that we are throwing the unsupported exception for setShardingKeyIfValid() + * @throws SQLException + * @throws TestAbortedException + * @since 1.9 + */ + @Test + public void setShardingKeyIfValidTest() throws TestAbortedException, SQLException { + assumeTrue(Util.supportJDBC43(connection)); + SQLServerConnection connection43 = (SQLServerConnection43) DriverManager.getConnection(connectionString); + try { + connection43.setShardingKeyIfValid(shardingKey, 10); + } + catch (SQLException e) { + assert (e.getMessage().contains("not implemented")); + } + try { + connection43.setShardingKeyIfValid(shardingKey, superShardingKey, 10); + } + catch (SQLException e) { + assert (e.getMessage().contains("not implemented")); + } + + } + + /** + * Tests that we are throwing the unsupported exception for setShardingKey() + * @throws SQLException + * @throws TestAbortedException + * @since 1.9 + */ + @Test + public void setShardingKeyTest() throws TestAbortedException, SQLException { + assumeTrue(Util.supportJDBC43(connection)); + SQLServerConnection connection43 = (SQLServerConnection43) DriverManager.getConnection(connectionString); + try { + connection43.setShardingKey(shardingKey); + } + catch (SQLException e) { + assert (e.getMessage().contains("not implemented")); + } + try { + connection43.setShardingKey(shardingKey, superShardingKey); + } + catch (SQLException e) { + assert (e.getMessage().contains("not implemented")); + } + + } + + /** + * Tests the stream drivers() methods in java.sql.DriverManager + * + * @since 1.9 + * @throws ClassNotFoundException + */ + @Test + public void driversTest() throws ClassNotFoundException { + Stream drivers = DriverManager.drivers(); + Object[] driversArray = drivers.toArray(); + assertEquals(driversArray[0].getClass(), Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver")); + } + + /** + * Tests deregister Driver + * + * @throws SQLException + * @throws ClassNotFoundException + */ + @Test + public void deregisterDriverTest() throws SQLException, ClassNotFoundException { + Enumeration drivers = DriverManager.getDrivers(); + Driver current = null; + while (drivers.hasMoreElements()) { + current = drivers.nextElement(); + DriverManager.deregisterDriver(current); + } + Stream currentDrivers = DriverManager.drivers(); + Object[] driversArray = currentDrivers.toArray(); + assertEquals(0, driversArray.length); + + DriverManager.registerDriver(current); + } + +} \ No newline at end of file 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 dad9c195f..2c4c191f2 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java @@ -133,11 +133,11 @@ public void inputParamsTest() throws SQLException { // the historical way: no leading '@', parameter names respected (not positional) CallableStatement cs1 = connection.prepareCall(call); - cs1.setString("p2", "bar"); - cs1.setString("p1", "foo"); + cs1.setString("p2", "world"); + cs1.setString("p1", "hello"); rs = cs1.executeQuery(); rs.next(); - assertEquals("foobar", rs.getString(1)); + assertEquals("helloworld", rs.getString(1)); // the "new" way: leading '@', parameter names still respected (not positional) CallableStatement cs2 = connection.prepareCall(call); @@ -150,7 +150,7 @@ public void inputParamsTest() throws SQLException { // sanity check: unrecognized parameter name CallableStatement cs3 = connection.prepareCall(call); try { - cs3.setString("@whatever", "junk"); + cs3.setString("@whatever", "test"); fail("SQLServerException should have been thrown"); } catch (SQLServerException sse) { if (!sse.getMessage().startsWith("Parameter @whatever was not defined")) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/DriverVersionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/DriverVersionTest.java index 911120ebb..bbd0ca5d6 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/DriverVersionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/DriverVersionTest.java @@ -12,13 +12,14 @@ import java.util.Arrays; import java.util.Random; -import javax.xml.bind.DatatypeConverter; 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.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.util.Util; /** * This test validates PR #342. In this PR, DatatypeConverter#parseHexBinary is replaced with type casting. This tests validates if the behavior @@ -38,12 +39,13 @@ public class DriverVersionTest extends AbstractTest { /** * validates version byte array generated by the original method and type casting reminds the same. + * @throws SQLServerException */ @Test - public void testConnectionDriver() { + public void testConnectionDriver() throws SQLServerException { // the original way to create version byte array String interfaceLibVersion = generateInterfaceLibVersion(); - byte originalVersionBytes[] = DatatypeConverter.parseHexBinary(interfaceLibVersion); + byte originalVersionBytes[] = Util.hexStringToByte(interfaceLibVersion); String originalBytes = Arrays.toString(originalVersionBytes); 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 5d06220b8..c1e7e3351 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/SSLProtocolTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/SSLProtocolTest.java @@ -66,11 +66,11 @@ 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 & TLSv1.2 should throw Exception"); + assertFalse(true, "Any protocol other than TLSv1, TLSv1.1, and TLSv1.2 should throw Exception"); } catch (SQLServerException e) { assertTrue(true, "Should throw exception"); - String errMsg = "SSL Protocol " + sslProtocol + " label is not valid. Only TLS, TLSv1, TLSv1.1 & TLSv1.2 are supported."; + 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()); } } 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 66ee1111b..8b99699b1 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/WarningTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/WarningTest.java @@ -13,6 +13,8 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.sql.SQLWarning; +import java.util.Arrays; +import java.util.List; import java.util.Properties; import org.junit.jupiter.api.Test; @@ -40,9 +42,18 @@ public void testWarnings() throws SQLException { } conn.setClientInfo(info2); warn = conn.getWarnings(); - for (int i = 4; i >= 0; i--) { - assertTrue(warn.toString().contains(infoArray[i]), "Warnings not found!"); + for (int i = 0; i < 5; i++) { + boolean found = false; + List list = Arrays.asList(infoArray); + for (String word : list) { + if (warn.toString().contains(word)) { + found = true; + break; + } + } + assertTrue(found, "warning : '" + warn.toString() + "' not found!"); warn = warn.getNextWarning(); + found = false; } conn.clearWarnings(); 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 667e3fc94..c1c12a8d9 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java @@ -40,8 +40,9 @@ public class BatchExecutionWithNullTest extends AbstractTest { static ResultSet rs = null; /** - * Test with combination of setString and setNull which cause the "Violation of PRIMARY KEY constraint and internally - * "Could not find prepared statement with handle X" error. + * Test with combination of setString and setNull which cause the "Violation of PRIMARY KEY constraint and internally "Could not find prepared + * statement with handle X" error. + * * @throws SQLException */ @Test @@ -118,7 +119,7 @@ public void testSetup() throws TestAbortedException, Exception { @AfterAll public static void terminateVariation() throws SQLException { connection = DriverManager.getConnection(connectionString); - + SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); Utils.dropTableIfExists("esimple", stmt); 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 54ebcdde2..41674e562 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 @@ -38,7 +38,7 @@ public class MergeTest extends AbstractTest { + " SELECT * FROM CricketTeams IF OBJECT_ID (N'dbo.CricketTeams_UpdatedList', N'U') IS NOT NULL DROP TABLE dbo.CricketTeams_UpdatedList;" + " CREATE TABLE dbo.CricketTeams_UpdatedList ( CricketTeamID tinyint NOT NULL PRIMARY KEY, CricketTeamCountry nvarchar(30), CricketTeamContinent nvarchar(50))" + "INSERT INTO dbo.CricketTeams_UpdatedList VALUES (1, 'Australia', 'Australia'), (2, 'India', 'Asia'), (3, 'Pakistan', 'Asia'), (4, 'Srilanka', 'Asia'), (5, 'Bangaladesh', 'Asia')," - + " (6, 'Hong Kong', 'Asia'), (8, 'England', 'Europe'), (9, 'South Africa', 'Africa'), (10, 'West Indies', 'North America'), (11, 'Zimbabwe', 'Africa');"; + + " (6, 'Thailand', 'Asia'), (8, 'England', 'Europe'), (9, 'South Africa', 'Africa'), (10, 'West Indies', 'North America'), (11, 'Zimbabwe', 'Africa');"; private static final String mergeCmd2 = "MERGE dbo.CricketTeams AS TARGET " + "USING dbo.CricketTeams_UpdatedList AS SOURCE " + "ON (TARGET.CricketTeamID = SOURCE.CricketTeamID) " + "WHEN MATCHED AND TARGET.CricketTeamContinent <> SOURCE.CricketTeamContinent OR " 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 afb311ae9..eb105a353 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 @@ -319,7 +319,7 @@ public void testCancelBlockedResponse() throws Exception { try { // Start a transaction on a second connection that locks the last part of the table - // and leave it hanging for now... + // and leave it non-responsive for now... conLock = DriverManager.getConnection(connectionString); conLock.setAutoCommit(false); stmtLock = conLock.createStatement(); @@ -434,7 +434,7 @@ public void testCancelBlockedResponsePS() throws Exception { try { // Start a transaction on a second connection that locks the last part of the table - // and leave it hanging for now... + // and leave it non-responsive for now... conLock = DriverManager.getConnection(connectionString); conLock.setAutoCommit(false); stmtLock = conLock.createStatement(); @@ -551,7 +551,7 @@ public void testCancelBlockedCursoredResponse() throws Exception { try { // Start a transaction on a second connection that locks the last part of the table - // and leave it hanging for now... + // and leave it non-responsive for now... conLock = DriverManager.getConnection(connectionString); conLock.setAutoCommit(false); stmtLock = conLock.createStatement(); @@ -726,11 +726,11 @@ public void testCancelGetOutParams() throws Exception { /** * Test that tries to flush out cancellation synchronization issues by repeatedly executing and cancelling statements on multiple threads. * - * Typical expected failures would be liveness issues (which would manifest as a test hang), incorrect results, or TDS corruption problems. + * Typical expected failures would be liveness issues (which would manifest as a test being non-responsive), incorrect results, or TDS corruption problems. * * A set of thread pairs runs for 10 seconds. Each pair has one thread repeatedly executing a SELECT statement and one thread repeatedly * cancelling execution of that statement. Nothing is done to validate whether any particular call to cancel had any affect on the statement. - * Liveness issues typically would manifest as a hang in this test. + * Liveness issues typically would manifest as a no response in this test. * * In order to maximize the likelihood of this test finding bugs, it should run on a multi-proc machine with the -server flag specified to the * JVM. Also, the debugging println statements are commented out deliberately to minimize the impact to the test from the diagnostics, which 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 f1e5167c4..336c2d304 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/util/Util.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/util/Util.java @@ -11,6 +11,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerConnection; import com.microsoft.sqlserver.jdbc.SQLServerDatabaseMetaData; +import com.microsoft.sqlserver.jdbc.SQLServerException; import com.microsoft.sqlserver.jdbc.SQLServerStatementColumnEncryptionSetting; /** @@ -289,4 +290,82 @@ public static boolean supportJDBC42(Connection con) throws SQLException { SQLServerDatabaseMetaData meta = (SQLServerDatabaseMetaData) con.getMetaData(); return (meta.getJDBCMajorVersion() >= 4 && meta.getJDBCMinorVersion() >= 2); } + + /** + * Utility function for checking if the system supports JDBC 4.3 + * + * @param con + * @return + */ + public static boolean supportJDBC43(Connection con) throws SQLException { + SQLServerDatabaseMetaData meta = (SQLServerDatabaseMetaData) con.getMetaData(); + return (meta.getJDBCMajorVersion() >= 4 && meta.getJDBCMinorVersion() >= 3); + } + + /** + * + * @param b + * byte value + * @param length + * length of the array + * @return + */ + final static char[] hexChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + public static String bytesToHexString(byte[] b, + int length) { + StringBuilder sb = new StringBuilder(length * 2); + for (int i = 0; i < length; i++) { + int hexVal = b[i] & 0xFF; + sb.append(hexChars[(hexVal & 0xF0) >> 4]); + sb.append(hexChars[(hexVal & 0x0F)]); + } + return sb.toString(); + } + + /** + * conversion routine valid values 0-9 a-f A-F throws exception when failed to convert + * + * @param value + * charArray + * @return + * @throws SQLServerException + */ + static byte CharToHex(char value) throws SQLServerException { + byte ret = 0; + if (value >= 'A' && value <= 'F') { + ret = (byte) (value - 'A' + 10); + } + else if (value >= 'a' && value <= 'f') { + ret = (byte) (value - 'a' + 10); + } + else if (value >= '0' && value <= '9') { + ret = (byte) (value - '0'); + } + else { + throw new IllegalArgumentException("The string is not in a valid hex format. "); + } + return ret; + } + + /** + * Converts a string to an array of bytes + * + * @param hexV + * a hexized string representation of bytes + * @return + * @throws SQLServerException + */ + public static byte[] hexStringToByte(String hexV) throws SQLServerException { + int len = hexV.length(); + char orig[] = hexV.toCharArray(); + if ((len % 2) != 0) { + throw new IllegalArgumentException("The string is not in a valid hex format: " + hexV); + } + byte[] bin = new byte[len / 2]; + for (int i = 0; i < len / 2; i++) { + bin[i] = (byte) ((CharToHex(orig[2 * i]) << 4) + CharToHex(orig[2 * i + 1])); + } + return bin; + } }