diff --git a/AppVeyorJCE/README.md b/AppVeyorJCE/README.md index db088d8636..994e013855 100644 --- a/AppVeyorJCE/README.md +++ b/AppVeyorJCE/README.md @@ -1,31 +1,31 @@ -# JCE chocolatey package - -### Disclaimers: -1. All contents within this directory originate from [this GitHub project](https://github.com/TobseF/jce-chocolatey-package). This project was added to allow us to test the Always Encrypted feature on AppVeyor builds. - -2. This is not an official project of Oracle. It\`s only easy of the manual installation: It downloads the JCE from oracle.com and unpacks it to the installed JDK. - - -[Chocolatey](https://chocolatey.org/) package for the [JCE (Unlimited Strength Java Cryptography Extension Policy Files)](http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html) - -This chocolatey package adds the JCE to latest installed Java SDK. The The `JAVA_HOME` environment variable has to point to the JDK. If `JAVA_HOME` is not set, nothing will be changed. The original files are backuped (renamed to `*_old`) and can be reverted at any time. This package is a perfect addion to the [JDK8 package](https://chocolatey.org/packages/jdk8). - -#### Install with [Chocolatey](https://chocolatey.org/) -```PowerShell -choco install jce -y -``` - -#### Build from source: -1. Install [Chocolatey](https://chocolatey.org/). -2. Open cmd with admin rights in jce package directory. -3. Pack NuGet Package (.nupkg). -```PowerShell -cpack -``` -4. Install JCE NuGet Package. -```PowerShell -choco install jce -fdv -s . -y -``` - - - +# JCE chocolatey package + +### Disclaimers: +1. All contents within this directory originate from [this GitHub project](https://github.com/TobseF/jce-chocolatey-package). This project was added to allow us to test the Always Encrypted feature on AppVeyor builds. + +2. This is not an official project of Oracle. It\`s only easy of the manual installation: It downloads the JCE from oracle.com and unpacks it to the installed JDK. + + +[Chocolatey](https://chocolatey.org/) package for the [JCE (Unlimited Strength Java Cryptography Extension Policy Files)](http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html) + +This chocolatey package adds the JCE to latest installed Java SDK. The The `JAVA_HOME` environment variable has to point to the JDK. If `JAVA_HOME` is not set, nothing will be changed. The original files are backuped (renamed to `*_old`) and can be reverted at any time. This package is a perfect addion to the [JDK8 package](https://chocolatey.org/packages/jdk8). + +#### Install with [Chocolatey](https://chocolatey.org/) +```PowerShell +choco install jce -y +``` + +#### Build from source: +1. Install [Chocolatey](https://chocolatey.org/). +2. Open cmd with admin rights in jce package directory. +3. Pack NuGet Package (.nupkg). +```PowerShell +cpack +``` +4. Install JCE NuGet Package. +```PowerShell +choco install jce -fdv -s . -y +``` + + + diff --git a/CHANGELOG.md b/CHANGELOG.md index 9da698da7d..ffd905d636 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) +## [6.3.0] Preview Release +### Added +- Added support for sql_variant datatype [#387](https://github.com/Microsoft/mssql-jdbc/pull/387) +- Added more Junit tests for AlwaysEncrpyted [#404](https://github.com/Microsoft/mssql-jdbc/pull/404) + +### Fixed Issues +- Fixed Turkey locale issue when lowercasing an "i" [#384](https://github.com/Microsoft/mssql-jdbc/pull/384) +- Fixed issue with incorrect parameter count for INSERT with subquery [#373](https://github.com/Microsoft/mssql-jdbc/pull/373) +- Fixed issue with running DDL in PreparedStatement [#372](https://github.com/Microsoft/mssql-jdbc/pull/372) +- Fixed issue with parameter metadata with whitespace characters [#371](https://github.com/Microsoft/mssql-jdbc/pull/371) +- Fixed handling of explicit boxing and unboxing [#84](https://github.com/Microsoft/mssql-jdbc/pull/84) +- Fixed metadata caching batch query issue [#393](https://github.com/Microsoft/mssql-jdbc/pull/393) +- Fixed javadoc issue for the newest maven version [#385](https://github.com/Microsoft/mssql-jdbc/pull/385) + ## [6.2.1] Hotfix & Stable Release ### Fixed Issues - Fixed queries without parameters using preparedStatement [#372](https://github.com/Microsoft/mssql-jdbc/pull/372) diff --git a/README.md b/README.md index ae22e0381e..1621e04553 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/Microsoft/mssql-jdbc/master/LICENSE) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.microsoft.sqlserver/mssql-jdbc/badge.svg)](http://mvnrepository.com/artifact/com.microsoft.sqlserver/mssql-jdbc) +[![codecov.io](http://codecov.io/github/Microsoft/mssql-jdbc/coverage.svg?branch=master)](http://codecov.io/github/Microsoft/mssql-jdbc?branch=master) [![Javadocs](http://javadoc.io/badge/com.microsoft.sqlserver/mssql-jdbc.svg)](http://javadoc.io/doc/com.microsoft.sqlserver/mssql-jdbc) [![Gitter](https://img.shields.io/gitter/room/badges/shields.svg)](https://gitter.im/Microsoft/mssql-developers)
@@ -61,6 +62,8 @@ To build the jar files, you must use Java 8 with Maven. You can choose to build ## Resources ### Documentation +API reference documentation is available in [Javadocs](https://aka.ms/jdbcjavadocs). + This driver is documented on [Microsoft's Documentation web site](https://msdn.microsoft.com/en-us/library/mt720657). ### Sample Code diff --git a/build.gradle b/build.gradle index 7a402dfbc6..e648bff700 100644 --- a/build.gradle +++ b/build.gradle @@ -80,4 +80,4 @@ dependencies { 'org.junit.jupiter:junit-jupiter-engine:5.0.0-M3', 'com.zaxxer:HikariCP:2.6.0', 'org.apache.commons:commons-dbcp2:2.1.1' -} +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 62b790dd7a..bd9d0ced66 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.microsoft.sqlserver mssql-jdbc - 6.2.0 + 6.3.0 jar @@ -48,14 +48,14 @@ com.microsoft.azure azure-keyvault - 0.9.7 + 1.0.0 true com.microsoft.azure adal4j - 1.1.3 + 1.2.0 true @@ -158,7 +158,7 @@ maven-jar-plugin 3.0.2 - ${project.artifactId}-${project.version}.jre7 + ${project.artifactId}-${project.version}.jre7-preview ${project.build.outputDirectory}/META-INF/MANIFEST.MF @@ -192,7 +192,7 @@ maven-jar-plugin 3.0.2 - ${project.artifactId}-${project.version}.jre8 + ${project.artifactId}-${project.version}.jre8-preview ${project.build.outputDirectory}/META-INF/MANIFEST.MF diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Column.java b/src/main/java/com/microsoft/sqlserver/jdbc/Column.java index 6a4a5d6708..a9c09e3305 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Column.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Column.java @@ -18,7 +18,16 @@ final class Column { private TypeInfo typeInfo; private CryptoMetadata cryptoMetadata; - + private SqlVariant internalVariant; + + final void setInternalVariant(SqlVariant type){ + this.internalVariant = type; + } + + final SqlVariant getInternalVariant(){ + return this.internalVariant; + } + final TypeInfo getTypeInfo() { return typeInfo; } @@ -187,11 +196,12 @@ Object getValue(JDBCType jdbcType, Calendar cal, TDSReader tdsReader) throws SQLServerException { Object value = getterDTV.getValue(jdbcType, typeInfo.getScale(), getterArgs, cal, typeInfo, cryptoMetadata, tdsReader); + setInternalVariant(getterDTV.getInternalVariant()); return (null != filter) ? filter.apply(value, jdbcType) : value; } int getInt(TDSReader tdsReader) throws SQLServerException { - return ((Integer) getValue(JDBCType.INTEGER, null, null, tdsReader)).intValue(); + return (Integer) getValue(JDBCType.INTEGER, null, null, tdsReader); } void updateValue(JDBCType jdbcType, diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/DDC.java b/src/main/java/com/microsoft/sqlserver/jdbc/DDC.java index 53c5b6f1c4..562b958c8d 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/DDC.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/DDC.java @@ -54,15 +54,15 @@ static final Object convertIntegerToObject(int intValue, StreamType streamType) { switch (jdbcType) { case INTEGER: - return new Integer(intValue); + return intValue; case SMALLINT: // 2.21 small and tinyint returned as short case TINYINT: - return new Short((short) intValue); + return (short) intValue; case BIT: case BOOLEAN: - return new Boolean(0 != intValue); + return 0 != intValue; case BIGINT: - return new Long(intValue); + return (long) intValue; case DECIMAL: case NUMERIC: case MONEY: @@ -70,9 +70,9 @@ static final Object convertIntegerToObject(int intValue, return new BigDecimal(Integer.toString(intValue)); case FLOAT: case DOUBLE: - return new Double(intValue); + return (double) intValue; case REAL: - return new Float(intValue); + return (float) intValue; case BINARY: return convertIntToBytes(intValue, valueLength); default: @@ -99,15 +99,15 @@ static final Object convertLongToObject(long longVal, StreamType streamType) { switch (jdbcType) { case BIGINT: - return new Long(longVal); + return longVal; case INTEGER: - return new Integer((int) longVal); + return (int) longVal; case SMALLINT: // small and tinyint returned as short case TINYINT: - return new Short((short) longVal); + return (short) longVal; case BIT: case BOOLEAN: - return new Boolean(0 != longVal); + return 0 != longVal; case DECIMAL: case NUMERIC: case MONEY: @@ -115,9 +115,9 @@ static final Object convertLongToObject(long longVal, return new BigDecimal(Long.toString(longVal)); case FLOAT: case DOUBLE: - return new Double(longVal); + return (double) longVal; case REAL: - return new Float(longVal); + return (float) longVal; case BINARY: byte[] convertedBytes = convertLongToBytes(longVal); int bytesToReturnLength; @@ -152,23 +152,23 @@ static final Object convertLongToObject(long longVal, case VARBINARY: switch (baseSSType) { case BIGINT: - return new Long(longVal); + return longVal; case INTEGER: - return new Integer((int) longVal); + return (int) longVal; case SMALLINT: // small and tinyint returned as short case TINYINT: - return new Short((short) longVal); + return (short) longVal; case BIT: - return new Boolean(0 != longVal); + return 0 != longVal; case DECIMAL: case NUMERIC: case MONEY: case SMALLMONEY: return new BigDecimal(Long.toString(longVal)); case FLOAT: - return new Double(longVal); + return (double) longVal; case REAL: - return new Float(longVal); + return (float) longVal; case BINARY: return convertLongToBytes(longVal); default: @@ -214,17 +214,17 @@ static final Object convertFloatToObject(float floatVal, StreamType streamType) { switch (jdbcType) { case REAL: - return new Float(floatVal); + return floatVal; case INTEGER: - return new Integer((int) floatVal); + return (int) floatVal; case SMALLINT: // small and tinyint returned as short case TINYINT: - return new Short((short) floatVal); + return (short) floatVal; case BIT: case BOOLEAN: - return new Boolean(0 != Float.compare(0.0f, floatVal)); + return 0 != Float.compare(0.0f, floatVal); case BIGINT: - return new Long((long) floatVal); + return (long) floatVal; case DECIMAL: case NUMERIC: case MONEY: @@ -232,7 +232,7 @@ static final Object convertFloatToObject(float floatVal, return new BigDecimal(Float.toString(floatVal)); case FLOAT: case DOUBLE: - return new Double((new Float(floatVal)).doubleValue()); + return (new Float(floatVal)).doubleValue(); case BINARY: return convertIntToBytes(Float.floatToRawIntBits(floatVal), 4); default: @@ -273,19 +273,19 @@ static final Object convertDoubleToObject(double doubleVal, switch (jdbcType) { case FLOAT: case DOUBLE: - return new Double(doubleVal); + return doubleVal; case REAL: - return new Float((new Double(doubleVal)).floatValue()); + return (new Double(doubleVal)).floatValue(); case INTEGER: - return new Integer((int) doubleVal); + return (int) doubleVal; case SMALLINT: // small and tinyint returned as short case TINYINT: - return new Short((short) doubleVal); + return (short) doubleVal; case BIT: case BOOLEAN: - return new Boolean(0 != Double.compare(0.0d, doubleVal)); + return 0 != Double.compare(0.0d, doubleVal); case BIGINT: - return new Long((long) doubleVal); + return (long) doubleVal; case DECIMAL: case NUMERIC: case MONEY: @@ -355,19 +355,19 @@ static final Object convertBigDecimalToObject(BigDecimal bigDecimalVal, return bigDecimalVal; case FLOAT: case DOUBLE: - return new Double(bigDecimalVal.doubleValue()); + return bigDecimalVal.doubleValue(); case REAL: - return new Float(bigDecimalVal.floatValue()); + return bigDecimalVal.floatValue(); case INTEGER: - return new Integer(bigDecimalVal.intValue()); + return bigDecimalVal.intValue(); case SMALLINT: // small and tinyint returned as short case TINYINT: - return new Short(bigDecimalVal.shortValue()); + return bigDecimalVal.shortValue(); case BIT: case BOOLEAN: - return new Boolean(0 != bigDecimalVal.compareTo(BigDecimal.valueOf(0))); + return 0 != bigDecimalVal.compareTo(BigDecimal.valueOf(0)); case BIGINT: - return new Long(bigDecimalVal.longValue()); + return bigDecimalVal.longValue(); case BINARY: return convertBigDecimalToBytes(bigDecimalVal, bigDecimalVal.scale()); default: @@ -400,19 +400,19 @@ static final Object convertMoneyToObject(BigDecimal bigDecimalVal, return bigDecimalVal; case FLOAT: case DOUBLE: - return new Double(bigDecimalVal.doubleValue()); + return bigDecimalVal.doubleValue(); case REAL: - return new Float(bigDecimalVal.floatValue()); + return bigDecimalVal.floatValue(); case INTEGER: - return new Integer(bigDecimalVal.intValue()); + return bigDecimalVal.intValue(); case SMALLINT: // small and tinyint returned as short case TINYINT: - return new Short(bigDecimalVal.shortValue()); + return bigDecimalVal.shortValue(); case BIT: case BOOLEAN: - return new Boolean(0 != bigDecimalVal.compareTo(BigDecimal.valueOf(0))); + return 0 != bigDecimalVal.compareTo(BigDecimal.valueOf(0)); case BIGINT: - return new Long(bigDecimalVal.longValue()); + return bigDecimalVal.longValue(); case BINARY: return convertToBytes(bigDecimalVal, bigDecimalVal.scale(), numberOfBytes); default: diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java index a7dc9b021c..914e382a29 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java @@ -95,7 +95,7 @@ static TDSType valueOf(int intValue) throws IllegalArgumentException { 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 = {intValue}; throw new IllegalArgumentException(form.format(msgArgs)); } @@ -145,7 +145,7 @@ enum SSType DECIMAL (Category.NUMERIC, "decimal", JDBCType.DECIMAL), NUMERIC (Category.NUMERIC, "numeric", JDBCType.NUMERIC), GUID (Category.GUID, "uniqueidentifier", JDBCType.GUID), - SQL_VARIANT (Category.VARIANT, "sql_variant", JDBCType.VARCHAR), + SQL_VARIANT (Category.SQL_VARIANT, "sql_variant", JDBCType.SQL_VARIANT), UDT (Category.UDT, "udt", JDBCType.VARBINARY), XML (Category.XML, "xml", JDBCType.LONGNVARCHAR), TIMESTAMP (Category.TIMESTAMP, "timestamp", JDBCType.BINARY); @@ -203,7 +203,7 @@ enum Category { TIME, TIMESTAMP, UDT, - VARIANT, + SQL_VARIANT, XML } @@ -358,7 +358,20 @@ enum GetterConversion SSType.Category.GUID, EnumSet.of( JDBCType.Category.BINARY, - JDBCType.Category.CHARACTER)); + JDBCType.Category.CHARACTER)), + + SQL_VARIANT ( + SSType.Category.SQL_VARIANT, + EnumSet.of( + JDBCType.Category.CHARACTER, + JDBCType.Category.SQL_VARIANT, + JDBCType.Category.NUMERIC, + JDBCType.Category.DATE, + JDBCType.Category.TIME, + JDBCType.Category.BINARY, + JDBCType.Category.TIMESTAMP, + JDBCType.Category.NCHARACTER, + JDBCType.Category.GUID)); private final SSType.Category from; private final EnumSet to; @@ -842,7 +855,9 @@ enum JDBCType TVP (Category.TVP, microsoft.sql.Types.STRUCTURED, "java.lang.Object"), DATETIME (Category.TIMESTAMP, microsoft.sql.Types.DATETIME, "java.sql.Timestamp"), SMALLDATETIME (Category.TIMESTAMP, microsoft.sql.Types.SMALLDATETIME, "java.sql.Timestamp"), - GUID (Category.CHARACTER, microsoft.sql.Types.GUID, "java.lang.String"); + GUID (Category.CHARACTER, microsoft.sql.Types.GUID, "java.lang.String"), + SQL_VARIANT (Category.SQL_VARIANT, microsoft.sql.Types.SQL_VARIANT, "java.lang.Object"); + final Category category; private final int intValue; @@ -889,7 +904,8 @@ enum Category { SQLXML, UNKNOWN, TVP, - GUID; + GUID, + SQL_VARIANT, } // This SetterConversion enum is based on the Category enum @@ -908,7 +924,8 @@ enum SetterConversion { JDBCType.Category.LONG_NCHARACTER, JDBCType.Category.BINARY, JDBCType.Category.LONG_BINARY, - JDBCType.Category.GUID)), + JDBCType.Category.GUID, + JDBCType.Category.SQL_VARIANT)), LONG_CHARACTER ( JDBCType.Category.LONG_CHARACTER, @@ -932,7 +949,8 @@ enum SetterConversion { EnumSet.of( JDBCType.Category.NCHARACTER, JDBCType.Category.LONG_NCHARACTER, - JDBCType.Category.NCLOB)), + JDBCType.Category.NCLOB, + JDBCType.Category.SQL_VARIANT)), LONG_NCHARACTER ( JDBCType.Category.LONG_NCHARACTER, @@ -960,7 +978,8 @@ enum SetterConversion { JDBCType.Category.BINARY, JDBCType.Category.LONG_BINARY, JDBCType.Category.BLOB, - JDBCType.Category.GUID)), + JDBCType.Category.GUID, + JDBCType.Category.SQL_VARIANT)), LONG_BINARY ( JDBCType.Category.LONG_BINARY, @@ -981,7 +1000,8 @@ enum SetterConversion { JDBCType.Category.CHARACTER, JDBCType.Category.LONG_CHARACTER, JDBCType.Category.NCHARACTER, - JDBCType.Category.LONG_NCHARACTER)), + JDBCType.Category.LONG_NCHARACTER, + JDBCType.Category.SQL_VARIANT)), DATE ( JDBCType.Category.DATE, @@ -992,7 +1012,8 @@ enum SetterConversion { JDBCType.Category.CHARACTER, JDBCType.Category.LONG_CHARACTER, JDBCType.Category.NCHARACTER, - JDBCType.Category.LONG_NCHARACTER)), + JDBCType.Category.LONG_NCHARACTER, + JDBCType.Category.SQL_VARIANT)), TIME ( JDBCType.Category.TIME, @@ -1003,7 +1024,8 @@ enum SetterConversion { JDBCType.Category.CHARACTER, JDBCType.Category.LONG_CHARACTER, JDBCType.Category.NCHARACTER, - JDBCType.Category.LONG_NCHARACTER)), + JDBCType.Category.LONG_NCHARACTER, + JDBCType.Category.SQL_VARIANT)), TIMESTAMP ( JDBCType.Category.TIMESTAMP, @@ -1015,7 +1037,8 @@ enum SetterConversion { JDBCType.Category.CHARACTER, JDBCType.Category.LONG_CHARACTER, JDBCType.Category.NCHARACTER, - JDBCType.Category.LONG_NCHARACTER)), + JDBCType.Category.LONG_NCHARACTER, + JDBCType.Category.SQL_VARIANT)), TIME_WITH_TIMEZONE ( JDBCType.Category.TIME_WITH_TIMEZONE, @@ -1103,7 +1126,8 @@ enum UpdaterConversion { SSType.Category.LONG_BINARY, SSType.Category.UDT, SSType.Category.GUID, - SSType.Category.TIMESTAMP)), + SSType.Category.TIMESTAMP, + SSType.Category.SQL_VARIANT)), LONG_CHARACTER ( JDBCType.Category.LONG_CHARACTER, @@ -1128,7 +1152,8 @@ enum UpdaterConversion { EnumSet.of( SSType.Category.NCHARACTER, SSType.Category.LONG_NCHARACTER, - SSType.Category.XML)), + SSType.Category.XML, + SSType.Category.SQL_VARIANT)), LONG_NCHARACTER ( JDBCType.Category.LONG_NCHARACTER, @@ -1157,7 +1182,8 @@ enum UpdaterConversion { SSType.Category.LONG_BINARY, SSType.Category.UDT, SSType.Category.TIMESTAMP, - SSType.Category.GUID)), + SSType.Category.GUID, + SSType.Category.SQL_VARIANT)), LONG_BINARY ( JDBCType.Category.LONG_BINARY, @@ -1184,7 +1210,8 @@ enum UpdaterConversion { SSType.Category.CHARACTER, SSType.Category.LONG_CHARACTER, SSType.Category.NCHARACTER, - SSType.Category.LONG_NCHARACTER)), + SSType.Category.LONG_NCHARACTER, + SSType.Category.SQL_VARIANT)), DATE ( JDBCType.Category.DATE, @@ -1196,7 +1223,8 @@ enum UpdaterConversion { SSType.Category.CHARACTER, SSType.Category.LONG_CHARACTER, SSType.Category.NCHARACTER, - SSType.Category.LONG_NCHARACTER)), + SSType.Category.LONG_NCHARACTER, + SSType.Category.SQL_VARIANT)), TIME ( JDBCType.Category.TIME, @@ -1208,7 +1236,8 @@ enum UpdaterConversion { SSType.Category.CHARACTER, SSType.Category.LONG_CHARACTER, SSType.Category.NCHARACTER, - SSType.Category.LONG_NCHARACTER)), + SSType.Category.LONG_NCHARACTER, + SSType.Category.SQL_VARIANT)), TIMESTAMP ( JDBCType.Category.TIMESTAMP, @@ -1221,7 +1250,8 @@ enum UpdaterConversion { SSType.Category.CHARACTER, SSType.Category.LONG_CHARACTER, SSType.Category.NCHARACTER, - SSType.Category.LONG_NCHARACTER)), + SSType.Category.LONG_NCHARACTER, + SSType.Category.SQL_VARIANT)), DATETIMEOFFSET ( JDBCType.Category.DATETIMEOFFSET, @@ -1259,8 +1289,13 @@ enum UpdaterConversion { SSType.Category.CHARACTER, SSType.Category.LONG_CHARACTER, SSType.Category.NCHARACTER, - SSType.Category.LONG_NCHARACTER)); - + SSType.Category.LONG_NCHARACTER)), + + SQL_VARIANT ( + JDBCType.Category.SQL_VARIANT, + EnumSet.of( + SSType.Category.SQL_VARIANT)); + private final JDBCType.Category from; private final EnumSet to; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/FailOverInfo.java b/src/main/java/com/microsoft/sqlserver/jdbc/FailOverInfo.java index 1f83263231..0e905bc7d4 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)).intValue(); + portNumber = new Integer(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 92638ffe06..01ad482566 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -134,6 +134,9 @@ final class TDS { static final int FLAG_TVP_DEFAULT_COLUMN = 0x200; static final int FEATURE_EXT_TERMINATOR = -1; + + // Sql_variant length + static final int SQL_VARIANT_LENGTH = 8009; static final String getTokenName(int tdsTokenType) { switch (tdsTokenType) { @@ -1383,7 +1386,7 @@ private final class HostNameOverrideX509TrustManager extends Object implements X this.logContext = tdsChannel.toString() + " (HostNameOverrideX509TrustManager):"; defaultTrustManager = tm; // canonical name is in lower case so convert this to lowercase too. - this.hostName = hostName.toLowerCase(); + this.hostName = hostName.toLowerCase(Locale.ENGLISH); ; } @@ -1506,13 +1509,11 @@ private void validateServerNameInCertificate(X509Certificate cert) throws Certif if (value != null && value instanceof String) { String dnsNameInSANCert = (String) value; - // convert to upper case and then to lower case in english locale - // to avoid Turkish i issues. + // Use English locale to avoid Turkish i issues. // Note that, this conversion was not necessary for // cert.getSubjectX500Principal().getName("canonical"); // as the above API already does this by default as per documentation. - dnsNameInSANCert = dnsNameInSANCert.toUpperCase(Locale.US); - dnsNameInSANCert = dnsNameInSANCert.toLowerCase(Locale.US); + dnsNameInSANCert = dnsNameInSANCert.toLowerCase(Locale.ENGLISH); isServerNameValidated = validateServerName(dnsNameInSANCert); @@ -2588,17 +2589,17 @@ private void findSocketUsingJavaNIO(InetAddress[] inetAddrs, } } - // if a channel was selected, make the necessary updates + // if a channel was selected, make the necessary updates if (selectedChannel != null) { - //the selectedChannel has the address that is connected successfully - //convert it to a java.net.Socket object with the address - SocketAddress iadd = selectedChannel.getRemoteAddress(); + // the selectedChannel has the address that is connected successfully + // convert it to a java.net.Socket object with the address + SocketAddress iadd = selectedChannel.getRemoteAddress(); selectedSocket = new Socket(); selectedSocket.connect(iadd); result = Result.SUCCESS; - - //close the channel since it is not used anymore + + // close the channel since it is not used anymore selectedChannel.close(); } } @@ -3238,6 +3239,17 @@ void writeByte(byte value) throws SQLServerException { } } + /** + * writing sqlCollation information for sqlVariant type when sending character types. + * + * @param variantType + * @throws SQLServerException + */ + void writeCollationForSqlVariant(SqlVariant variantType) throws SQLServerException { + writeInt(variantType.getCollation().getCollationInfo()); + writeByte((byte) (variantType.getCollation().getCollationSortID() & 0xFF)); + } + void writeChar(char value) throws SQLServerException { if (stagingBuffer.remaining() >= 2) { stagingBuffer.putChar(value); @@ -3293,7 +3305,7 @@ void writeInt(int value) throws SQLServerException { * the data value */ void writeReal(Float value) throws SQLServerException { - writeInt(Float.floatToRawIntBits(value.floatValue())); + writeInt(Float.floatToRawIntBits(value)); } /** @@ -3364,6 +3376,64 @@ void writeBigDecimal(BigDecimal bigDecimalVal, System.arraycopy(valueBytes, 2, bytes, 0, valueBytes.length - 2); writeBytes(bytes); } + + /** + * Append a big decimal inside sql_variant in the TDS stream. + * + * @param bigDecimalVal + * the big decimal data value + * @param srcJdbcType + * the source JDBCType + */ + void writeSqlVariantInternalBigDecimal(BigDecimal bigDecimalVal, + int srcJdbcType) throws SQLServerException { + /* + * Length including sign byte One 1-byte unsigned integer that represents the sign of the decimal value (0 => Negative, 1 => positive) One + * 16-byte signed integer that represents the decimal value multiplied by 10^scale. In sql_variant, we send the bigdecimal with precision 38, + * therefore we use 16 bytes for the maximum size of this integer. + */ + + boolean isNegative = (bigDecimalVal.signum() < 0); + BigInteger bi = bigDecimalVal.unscaledValue(); + if (isNegative) + { + bi = bi.negate(); + } + int bLength; + bLength = BYTES16; + + writeByte((byte) (isNegative ? 0 : 1)); + + // Get the bytes of the BigInteger value. It is in reverse order, with + // most significant byte in 0-th element. We need to reverse it first before sending over TDS. + byte[] unscaledBytes = bi.toByteArray(); + + if (unscaledBytes.length > bLength) { + // If precession of input is greater than maximum allowed (p><= 38) throw Exception + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_valueOutOfRange")); + Object[] msgArgs = {JDBCType.of(srcJdbcType)}; + throw new SQLServerException(form.format(msgArgs), SQLState.DATA_EXCEPTION_LENGTH_MISMATCH, DriverError.NOT_SET, null); + } + + // Byte array to hold all the reversed and padding bytes. + byte[] bytes = new byte[bLength]; + + // We need to fill up the rest of the array with zeros, as unscaledBytes may have less bytes + // than the required size for TDS. + int remaining = bLength - unscaledBytes.length; + + // Reverse the bytes. + int i, j; + for (i = 0, j = unscaledBytes.length - 1; i < unscaledBytes.length;) + bytes[i++] = unscaledBytes[j--]; + + // Fill the rest of the array with zeros. + for (; i < remaining; i++) + { + bytes[i] = (byte) 0x00; + } + writeBytes(bytes); + } void writeSmalldatetime(String value) throws SQLServerException { GregorianCalendar calendar = initializeCalender(TimeZone.getDefault()); @@ -3781,7 +3851,7 @@ void writeStream(InputStream inputStream, // the actual stream length did not match then cancel the request. if (DataTypes.UNKNOWN_STREAM_LENGTH != advertisedLength && actualLength != advertisedLength) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_mismatchedStreamLength")); - Object[] msgArgs = {Long.valueOf(advertisedLength), Long.valueOf(actualLength)}; + Object[] msgArgs = {advertisedLength, actualLength}; error(form.format(msgArgs), SQLState.DATA_EXCEPTION_LENGTH_MISMATCH, DriverError.NOT_SET); } } @@ -3866,10 +3936,10 @@ void writeNonUnicodeReader(Reader reader, // the actual stream length did not match then cancel the request. if (DataTypes.UNKNOWN_STREAM_LENGTH != advertisedLength && actualLength != advertisedLength) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_mismatchedStreamLength")); - Object[] msgArgs = {Long.valueOf(advertisedLength), Long.valueOf(actualLength)}; + Object[] msgArgs = {advertisedLength, actualLength}; error(form.format(msgArgs), SQLState.DATA_EXCEPTION_LENGTH_MISMATCH, DriverError.NOT_SET); } - } + } /* * Note: There is another method with same code logic for non unicode reader, writeNonUnicodeReader(), implemented for performance efficiency. Any @@ -3931,7 +4001,7 @@ void writeReader(Reader reader, // the actual stream length did not match then cancel the request. if (DataTypes.UNKNOWN_STREAM_LENGTH != advertisedLength && actualLength != advertisedLength) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_mismatchedStreamLength")); - Object[] msgArgs = {Long.valueOf(advertisedLength), Long.valueOf(actualLength)}; + Object[] msgArgs = {advertisedLength, actualLength}; error(form.format(msgArgs), SQLState.DATA_EXCEPTION_LENGTH_MISMATCH, DriverError.NOT_SET); } } @@ -4153,7 +4223,7 @@ void writeRPCBit(String sName, } else { writeByte((byte) 1); // length of datatype - writeByte((byte) (booleanValue.booleanValue() ? 1 : 0)); + writeByte((byte) (booleanValue ? 1 : 0)); } } @@ -4177,7 +4247,7 @@ void writeRPCByte(String sName, } else { writeByte((byte) 1); // length of datatype - writeByte(byteValue.byteValue()); + writeByte(byteValue); } } @@ -4201,7 +4271,7 @@ void writeRPCShort(String sName, } else { writeByte((byte) 2); // length of datatype - writeShort(shortValue.shortValue()); + writeShort(shortValue); } } @@ -4225,7 +4295,7 @@ void writeRPCInt(String sName, } else { writeByte((byte) 4); // length of datatype - writeInt(intValue.intValue()); + writeInt(intValue); } } @@ -4249,7 +4319,7 @@ void writeRPCLong(String sName, } else { writeByte((byte) 8); // length of datatype - writeLong(longValue.longValue()); + writeLong(longValue); } } @@ -4276,7 +4346,19 @@ void writeRPCReal(String sName, else { writeByte((byte) 4); // max length writeByte((byte) 4); // actual length - writeInt(Float.floatToRawIntBits(floatValue.floatValue())); + writeInt(Float.floatToRawIntBits(floatValue)); + } + } + + void writeRPCSqlVariant(String sName, + SqlVariant sqlVariantValue, + boolean bOut) throws SQLServerException { + writeRPCNameValType(sName, bOut, TDSType.SQL_VARIANT); + + // Data and length + if (null == sqlVariantValue) { + writeInt(0); // max length + writeInt(0); // actual length } } @@ -4304,7 +4386,7 @@ void writeRPCDouble(String sName, } else { writeByte((byte) l); // len of data bytes - long bits = Double.doubleToLongBits(doubleValue.doubleValue()); + long bits = Double.doubleToLongBits(doubleValue); long mask = 0xFF; int nShift = 0; for (int i = 0; i < 8; i++) { @@ -4526,9 +4608,6 @@ void writeTVP(TVP value) throws SQLServerException { } void writeTVPRows(TVP value) throws SQLServerException { - boolean isShortValue, isNull; - int dataLength; - boolean tdsWritterCached = false; ByteBuffer cachedTVPHeaders = null; TDSCommand cachedCommand = null; @@ -4536,7 +4615,7 @@ void writeTVPRows(TVP value) throws SQLServerException { boolean cachedRequestComplete = false; boolean cachedInterruptsEnabled = false; boolean cachedProcessedResponse = false; - + if (!value.isNull()) { // If the preparedStatement and the ResultSet are created by the same connection, and TVP is set with ResultSet and Server Cursor @@ -4566,12 +4645,12 @@ void writeTVPRows(TVP value) throws SQLServerException { } } } - + Map columnMetadata = value.getColumnMetadata(); Iterator> columnsIterator; while (value.next()) { - + // restore command and TDS header, which have been overwritten by value.next() if (tdsWritterCached) { command = cachedCommand; @@ -4580,7 +4659,7 @@ void writeTVPRows(TVP value) throws SQLServerException { logBuffer.clear(); writeBytes(cachedTVPHeaders.array(), 0, cachedTVPHeaders.position()); } - + Object[] rowData = value.getRowData(); // ROW @@ -4609,201 +4688,7 @@ void writeTVPRows(TVP value) throws SQLServerException { } } } - try { - switch (jdbcType) { - case BIGINT: - if (null == currentColumnStringValue) - writeByte((byte) 0); - else { - writeByte((byte) 8); - writeLong(Long.valueOf(currentColumnStringValue).longValue()); - } - break; - - case BIT: - if (null == currentColumnStringValue) - writeByte((byte) 0); - else { - writeByte((byte) 1); - writeByte((byte) (Boolean.valueOf(currentColumnStringValue).booleanValue() ? 1 : 0)); - } - break; - - case INTEGER: - if (null == currentColumnStringValue) - writeByte((byte) 0); - else { - writeByte((byte) 4); - writeInt(Integer.valueOf(currentColumnStringValue).intValue()); - } - break; - - case SMALLINT: - case TINYINT: - if (null == currentColumnStringValue) - writeByte((byte) 0); - else { - writeByte((byte) 2); // length of datatype - writeShort(Short.valueOf(currentColumnStringValue).shortValue()); - } - break; - - case DECIMAL: - case NUMERIC: - if (null == currentColumnStringValue) - writeByte((byte) 0); - else { - writeByte((byte) TDSWriter.BIGDECIMAL_MAX_LENGTH); // maximum length - BigDecimal bdValue = new BigDecimal(currentColumnStringValue); - - /* - * setScale of all BigDecimal value based on metadata as scale is not sent seperately for individual value. Use - * the rounding used in Server. Say, for BigDecimal("0.1"), if scale in metdadata is 0, then ArithmeticException - * would be thrown if RoundingMode is not set - */ - bdValue = bdValue.setScale(columnPair.getValue().scale, RoundingMode.HALF_UP); - - byte[] valueBytes = DDC.convertBigDecimalToBytes(bdValue, bdValue.scale()); - - // 1-byte for sign and 16-byte for integer - byte[] byteValue = new byte[17]; - - // removing the precision and scale information from the valueBytes array - System.arraycopy(valueBytes, 2, byteValue, 0, valueBytes.length - 2); - writeBytes(byteValue); - } - break; - - case DOUBLE: - if (null == currentColumnStringValue) - writeByte((byte) 0); // len of data bytes - else { - writeByte((byte) 8); // len of data bytes - long bits = Double.doubleToLongBits(Double.valueOf(currentColumnStringValue).doubleValue()); - long mask = 0xFF; - int nShift = 0; - for (int i = 0; i < 8; i++) { - writeByte((byte) ((bits & mask) >> nShift)); - nShift += 8; - mask = mask << 8; - } - } - break; - - case FLOAT: - case REAL: - if (null == currentColumnStringValue) - writeByte((byte) 0); // actual length (0 == null) - else { - writeByte((byte) 4); // actual length - writeInt(Float.floatToRawIntBits(Float.valueOf(currentColumnStringValue).floatValue())); - } - break; - - case DATE: - case TIME: - case TIMESTAMP: - case DATETIMEOFFSET: - case TIMESTAMP_WITH_TIMEZONE: - case TIME_WITH_TIMEZONE: - case CHAR: - case VARCHAR: - case NCHAR: - case NVARCHAR: - case LONGVARCHAR: - case LONGNVARCHAR: - case SQLXML: - isShortValue = (2L * columnPair.getValue().precision) <= DataTypes.SHORT_VARTYPE_MAX_BYTES; - isNull = (null == currentColumnStringValue); - dataLength = isNull ? 0 : currentColumnStringValue.length() * 2; - if (!isShortValue) { - // check null - if (isNull) - // Null header for v*max types is 0xFFFFFFFFFFFFFFFF. - writeLong(0xFFFFFFFFFFFFFFFFL); - else if (DataTypes.UNKNOWN_STREAM_LENGTH == dataLength) - // Append v*max length. - // UNKNOWN_PLP_LEN is 0xFFFFFFFFFFFFFFFE - writeLong(0xFFFFFFFFFFFFFFFEL); - else - // For v*max types with known length, length is - writeLong(dataLength); - if (!isNull) { - if (dataLength > 0) { - writeInt(dataLength); - writeString(currentColumnStringValue); - } - // Send the terminator PLP chunk. - writeInt(0); - } - } - else { - if (isNull) - writeShort((short) -1); // actual len - else { - writeShort((short) dataLength); - writeString(currentColumnStringValue); - } - } - break; - - case BINARY: - case VARBINARY: - case LONGVARBINARY: - // Handle conversions as done in other types. - isShortValue = columnPair.getValue().precision <= DataTypes.SHORT_VARTYPE_MAX_BYTES; - isNull = (null == currentObject); - if (currentObject instanceof String) - dataLength = isNull ? 0 : (toByteArray(currentObject.toString())).length; - else - dataLength = isNull ? 0 : ((byte[]) currentObject).length; - if (!isShortValue) { - // check null - if (isNull) - // Null header for v*max types is 0xFFFFFFFFFFFFFFFF. - writeLong(0xFFFFFFFFFFFFFFFFL); - else if (DataTypes.UNKNOWN_STREAM_LENGTH == dataLength) - // Append v*max length. - // UNKNOWN_PLP_LEN is 0xFFFFFFFFFFFFFFFE - writeLong(0xFFFFFFFFFFFFFFFEL); - else - // For v*max types with known length, length is - writeLong(dataLength); - if (!isNull) { - if (dataLength > 0) { - writeInt(dataLength); - if (currentObject instanceof String) - writeBytes(toByteArray(currentObject.toString())); - else - writeBytes((byte[]) currentObject); - } - // Send the terminator PLP chunk. - writeInt(0); - } - } - else { - if (isNull) - writeShort((short) -1); // actual len - else { - writeShort((short) dataLength); - if (currentObject instanceof String) - writeBytes(toByteArray(currentObject.toString())); - else - writeBytes((byte[]) currentObject); - } - } - break; - - default: - assert false : "Unexpected JDBC type " + jdbcType.toString(); - } - } - catch (IllegalArgumentException e) { - throw new SQLServerException(SQLServerException.getErrString("R_errorConvertingValue"), e); - } - catch (ArrayIndexOutOfBoundsException e) { - throw new SQLServerException(SQLServerException.getErrString("R_CSVDataSchemaMismatch"), e); - } + writeInternalTVPRowValues(jdbcType, currentColumnStringValue, currentObject, columnPair, false); currentColumn++; } @@ -4842,6 +4727,303 @@ else if (DataTypes.UNKNOWN_STREAM_LENGTH == dataLength) } } + private void writeInternalTVPRowValues(JDBCType jdbcType, + String currentColumnStringValue, + Object currentObject, + Map.Entry columnPair, + boolean isSqlVariant) throws SQLServerException { + boolean isShortValue, isNull; + int dataLength; + switch (jdbcType) { + case BIGINT: + if (null == currentColumnStringValue) + writeByte((byte) 0); + else { + if (isSqlVariant) { + writeTVPSqlVariantHeader(10, TDSType.INT8.byteValue(), (byte) 0); + } + else { + writeByte((byte) 8); + } + writeLong(Long.valueOf(currentColumnStringValue).longValue()); + } + break; + + case BIT: + if (null == currentColumnStringValue) + writeByte((byte) 0); + else { + if (isSqlVariant) + writeTVPSqlVariantHeader(3, TDSType.BIT1.byteValue(), (byte) 0); + else + writeByte((byte) 1); + writeByte((byte) (Boolean.valueOf(currentColumnStringValue).booleanValue() ? 1 : 0)); + } + break; + + case INTEGER: + if (null == currentColumnStringValue) + writeByte((byte) 0); + else { + if (!isSqlVariant) + writeByte((byte) 4); + else + writeTVPSqlVariantHeader(6, TDSType.INT4.byteValue(), (byte) 0); + writeInt(Integer.valueOf(currentColumnStringValue).intValue()); + } + break; + + case SMALLINT: + case TINYINT: + if (null == currentColumnStringValue) + writeByte((byte) 0); + else { + if (isSqlVariant) { + writeTVPSqlVariantHeader(6, TDSType.INT4.byteValue(), (byte) 0); + writeInt(Integer.valueOf(currentColumnStringValue)); + } + else { + writeByte((byte) 2); // length of datatype + writeShort(Short.valueOf(currentColumnStringValue).shortValue()); + } + } + break; + + case DECIMAL: + case NUMERIC: + if (null == currentColumnStringValue) + writeByte((byte) 0); + else { + if (isSqlVariant) { + writeTVPSqlVariantHeader(21, TDSType.DECIMALN.byteValue(), (byte) 2); + writeByte((byte) 38); // scale (byte)variantType.getScale() + writeByte((byte) 4); // scale (byte)variantType.getScale() + } + else { + writeByte((byte) TDSWriter.BIGDECIMAL_MAX_LENGTH); // maximum length + } + BigDecimal bdValue = new BigDecimal(currentColumnStringValue); + + /* + * setScale of all BigDecimal value based on metadata as scale is not sent seperately for individual value. Use the rounding used + * in Server. Say, for BigDecimal("0.1"), if scale in metdadata is 0, then ArithmeticException would be thrown if RoundingMode is + * not set + */ + bdValue = bdValue.setScale(columnPair.getValue().scale, RoundingMode.HALF_UP); + + byte[] valueBytes = DDC.convertBigDecimalToBytes(bdValue, bdValue.scale()); + + // 1-byte for sign and 16-byte for integer + byte[] byteValue = new byte[17]; + + // removing the precision and scale information from the valueBytes array + System.arraycopy(valueBytes, 2, byteValue, 0, valueBytes.length - 2); + writeBytes(byteValue); + } + break; + + case DOUBLE: + if (null == currentColumnStringValue) + writeByte((byte) 0); // len of data bytes + else { + if (isSqlVariant) { + writeTVPSqlVariantHeader(10, TDSType.FLOAT8.byteValue(), (byte) 0); + writeDouble(Double.valueOf(currentColumnStringValue.toString())); + break; + } + writeByte((byte) 8); // len of data bytes + long bits = Double.doubleToLongBits(Double.valueOf(currentColumnStringValue).doubleValue()); + long mask = 0xFF; + int nShift = 0; + for (int i = 0; i < 8; i++) { + writeByte((byte) ((bits & mask) >> nShift)); + nShift += 8; + mask = mask << 8; + } + } + break; + + case FLOAT: + case REAL: + if (null == currentColumnStringValue) + writeByte((byte) 0); + else { + if (isSqlVariant) { + writeTVPSqlVariantHeader(6, TDSType.FLOAT4.byteValue(), (byte) 0); + writeInt(Float.floatToRawIntBits(Float.valueOf(currentColumnStringValue).floatValue())); + } + else { + writeByte((byte) 4); + writeInt(Float.floatToRawIntBits(Float.valueOf(currentColumnStringValue).floatValue())); + } + } + break; + + case DATE: + case TIME: + case TIMESTAMP: + case DATETIMEOFFSET: + case TIMESTAMP_WITH_TIMEZONE: + case TIME_WITH_TIMEZONE: + case CHAR: + case VARCHAR: + case NCHAR: + case NVARCHAR: + case LONGVARCHAR: + case LONGNVARCHAR: + case SQLXML: + isShortValue = (2L * columnPair.getValue().precision) <= DataTypes.SHORT_VARTYPE_MAX_BYTES; + isNull = (null == currentColumnStringValue); + dataLength = isNull ? 0 : currentColumnStringValue.length() * 2; + if (!isShortValue) { + // check null + if (isNull) { + // Null header for v*max types is 0xFFFFFFFFFFFFFFFF. + writeLong(0xFFFFFFFFFFFFFFFFL); + } + else if (isSqlVariant) { + // for now we send as bigger type, but is sendStringParameterAsUnicoe is set to false we can't send nvarchar + // since we are writing as nvarchar we need to write as tdstype.bigvarchar value because if we + // want to supprot varchar(8000) it becomes as nvarchar, 8000*2 therefore we should send as longvarchar, + // but we cannot send more than 8000 cause sql_variant datatype in sql server does not support it. + // then throw exception if user is sending more than that + if (dataLength > 2 * DataTypes.SHORT_VARTYPE_MAX_BYTES) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidStringValue")); + throw new SQLServerException(null, form.format(new Object[] {}), null, 0, false); + } + int length = currentColumnStringValue.length(); + writeTVPSqlVariantHeader(9 + length, TDSType.BIGVARCHAR.byteValue(), (byte) 0x07); + SQLCollation col = con.getDatabaseCollation(); + // write collation for sql variant + writeInt(col.getCollationInfo()); + writeByte((byte) col.getCollationSortID()); + writeShort((short) (length)); + writeBytes(currentColumnStringValue.getBytes()); + break; + } + + else if (DataTypes.UNKNOWN_STREAM_LENGTH == dataLength) + // Append v*max length. + // UNKNOWN_PLP_LEN is 0xFFFFFFFFFFFFFFFE + writeLong(0xFFFFFFFFFFFFFFFEL); + else + // For v*max types with known length, length is + writeLong(dataLength); + if (!isNull) { + if (dataLength > 0) { + writeInt(dataLength); + writeString(currentColumnStringValue); + } + // Send the terminator PLP chunk. + writeInt(0); + } + } + else { + if (isNull) + writeShort((short) -1); // actual len + else { + if (isSqlVariant) { + // for now we send as bigger type, but is sendStringParameterAsUnicoe is set to false we can't send nvarchar + // check for this + int length = currentColumnStringValue.length() * 2; + writeTVPSqlVariantHeader(9 + length, TDSType.NVARCHAR.byteValue(), (byte) 7); + SQLCollation col = con.getDatabaseCollation(); + // write collation for sql variant + writeInt(col.getCollationInfo()); + writeByte((byte) col.getCollationSortID()); + int stringLength = currentColumnStringValue.length(); + byte[] typevarlen = new byte[2]; + typevarlen[0] = (byte) (2 * stringLength & 0xFF); + typevarlen[1] = (byte) ((2 * stringLength >> 8) & 0xFF); + writeBytes(typevarlen); + writeString(currentColumnStringValue); + break; + } + else { + writeShort((short) dataLength); + writeString(currentColumnStringValue); + } + } + } + break; + + case BINARY: + case VARBINARY: + case LONGVARBINARY: + // Handle conversions as done in other types. + isShortValue = columnPair.getValue().precision <= DataTypes.SHORT_VARTYPE_MAX_BYTES; + isNull = (null == currentObject); + if (currentObject instanceof String) + dataLength = isNull ? 0 : (toByteArray(currentObject.toString())).length; + else + dataLength = isNull ? 0 : ((byte[]) currentObject).length; + if (!isShortValue) { + // check null + if (isNull) + // Null header for v*max types is 0xFFFFFFFFFFFFFFFF. + writeLong(0xFFFFFFFFFFFFFFFFL); + else if (DataTypes.UNKNOWN_STREAM_LENGTH == dataLength) + // Append v*max length. + // UNKNOWN_PLP_LEN is 0xFFFFFFFFFFFFFFFE + writeLong(0xFFFFFFFFFFFFFFFEL); + else + // For v*max types with known length, length is + writeLong(dataLength); + if (!isNull) { + if (dataLength > 0) { + writeInt(dataLength); + if (currentObject instanceof String) + writeBytes(toByteArray(currentObject.toString())); + else + writeBytes((byte[]) currentObject); + } + // Send the terminator PLP chunk. + writeInt(0); + } + } + else { + if (isNull) + writeShort((short) -1); // actual len + else { + writeShort((short) dataLength); + if (currentObject instanceof String) + writeBytes(toByteArray(currentObject.toString())); + else + writeBytes((byte[]) currentObject); + } + } + break; + case SQL_VARIANT: + boolean isShiloh = (8 >= con.getServerMajorVersion() ? true : false); + if (isShiloh) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_SQLVariantSupport")); + throw new SQLServerException(null, form.format(new Object[] {}), null, 0, false); + } + JDBCType internalJDBCType; + JavaType javaType = JavaType.of(currentObject); + internalJDBCType = javaType.getJDBCType(SSType.UNKNOWN, jdbcType); + writeInternalTVPRowValues(internalJDBCType, currentColumnStringValue, currentObject, columnPair, true); + break; + default: + assert false : "Unexpected JDBC type " + jdbcType.toString(); + } + } + + /** + * writes Header for sql_variant for TVP + * @param length + * @param tdsType + * @param probBytes + * @throws SQLServerException + */ + private void writeTVPSqlVariantHeader(int length, + byte tdsType, + byte probBytes) throws SQLServerException { + writeInt(length); + writeByte(tdsType); + writeByte(probBytes); + } + private static byte[] toByteArray(String s) { return DatatypeConverter.parseHexBinary(s); } @@ -4958,6 +5140,11 @@ void writeTVPColumnMetaData(TVP value) throws SQLServerException { else // non PLP writeShort((short) DataTypes.SHORT_VARTYPE_MAX_BYTES); break; + case SQL_VARIANT: + writeByte(TDSType.SQL_VARIANT.byteValue()); + writeInt(TDS.SQL_VARIANT_LENGTH);// write length of sql variant 8009 + + break; default: assert false : "Unexpected JDBC type " + jdbcType.toString(); @@ -5525,6 +5712,7 @@ void writeRPCDateTimeOffset(String sName, writeShort((short) minutesOffset); } + /** * Returns subSecondNanos rounded to the maximum precision supported. The maximum fractional scale is MAX_FRACTIONAL_SECONDS_SCALE(7). Eg1: if you * pass 456,790,123 the function would return 456,790,100 Eg2: if you pass 456,790,150 the function would return 456,790,200 Eg3: if you pass @@ -5962,7 +6150,7 @@ void writeRPCInputStream(String sName, if (streamLength >= maxStreamLength) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidLength")); - Object[] msgArgs = {Long.valueOf(streamLength)}; + Object[] msgArgs = {streamLength}; SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), "", true); } @@ -6928,11 +7116,12 @@ final class TimeoutTimer implements Runnable { private final int timeoutSeconds; private final TDSCommand command; private volatile Future task; - + private static final ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactory() { private final ThreadGroup tg = new ThreadGroup(threadGroupName); private final String threadNamePrefix = tg.getName() + "-"; private final AtomicInteger threadNumber = new AtomicInteger(0); + @Override public Thread newThread(Runnable r) { Thread t = new Thread(tg, r, threadNamePrefix + threadNumber.incrementAndGet()); @@ -6940,7 +7129,7 @@ public Thread newThread(Runnable r) { return t; } }); - + private volatile boolean canceled = false; TimeoutTimer(int timeoutSeconds, @@ -6961,7 +7150,7 @@ final void stop() { canceled = true; } - public void run() { + public void run() { int secondsRemaining = timeoutSeconds; try { // Poll every second while time is left on the timer. diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java b/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java index 70d99421a1..d0b5693c3d 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java @@ -8,13 +8,14 @@ package com.microsoft.sqlserver.jdbc; -import java.util.Map; - -import org.apache.http.Header; -import org.apache.http.message.BasicHeader; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import com.microsoft.aad.adal4j.AuthenticationContext; +import com.microsoft.aad.adal4j.AuthenticationResult; +import com.microsoft.aad.adal4j.ClientCredential; import com.microsoft.azure.keyvault.authentication.KeyVaultCredentials; -import com.microsoft.windowsazure.core.pipeline.filter.ServiceRequestContext; /** * @@ -23,42 +24,46 @@ */ class KeyVaultCredential extends KeyVaultCredentials { - // this is the only supported access token type - // https://msdn.microsoft.com/en-us/library/azure/dn645538.aspx - private final String accessTokenType = "Bearer"; - - SQLServerKeyVaultAuthenticationCallback authenticationCallback = null; String clientId = null; String clientKey = null; - String accessToken = null; - KeyVaultCredential(SQLServerKeyVaultAuthenticationCallback authenticationCallback) { - this.authenticationCallback = authenticationCallback; + KeyVaultCredential(String clientId, + String clientKey) { + this.clientId = clientId; + this.clientKey = clientKey; } - /** - * Authenticates the service request - * - * @param request - * the ServiceRequestContext - * @param challenge - * used to get the accessToken - * @return BasicHeader - */ - @Override - public Header doAuthenticate(ServiceRequestContext request, - Map challenge) { - assert null != challenge; - - String authorization = challenge.get("authorization"); - String resource = challenge.get("resource"); - - accessToken = authenticationCallback.getAccessToken(authorization, resource, ""); - return new BasicHeader("Authorization", accessTokenType + " " + accessToken); + public String doAuthenticate(String authorization, + String resource, + String scope) { + AuthenticationResult token = getAccessTokenFromClientCredentials(authorization, resource, clientId, clientKey); + return token.getAccessToken(); } - void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } + private static AuthenticationResult getAccessTokenFromClientCredentials(String authorization, + String resource, + String clientId, + String clientKey) { + AuthenticationContext context = null; + AuthenticationResult result = null; + ExecutorService service = null; + try { + service = Executors.newFixedThreadPool(1); + context = new AuthenticationContext(authorization, false, service); + ClientCredential credentials = new ClientCredential(clientId, clientKey); + Future future = context.acquireToken(resource, credentials, null); + result = future.get(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + finally { + service.shutdown(); + } + if (result == null) { + throw new RuntimeException("authentication result was null"); + } + return result; + } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java index 87ddcf88c2..afb71cd7fa 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java @@ -26,6 +26,7 @@ import java.util.Calendar; import java.util.Locale; + /** * Parameter represents a JDBC parameter value that is supplied with a prepared or callable statement or an updatable result set. Parameter is JDBC * type specific and is capable of representing any Java native type as well as a number of Java object types including binary and character streams. @@ -253,7 +254,7 @@ void setFromReturnStatus(int returnStatus, if (null == getterDTV) getterDTV = new DTV(); - getterDTV.setValue(null, JDBCType.INTEGER, new Integer(returnStatus), JavaType.INTEGER, null, null, null, con, getForceEncryption()); + getterDTV.setValue(null, JDBCType.INTEGER, returnStatus, JavaType.INTEGER, null, null, null, con, getForceEncryption()); } void setValue(JDBCType jdbcType, @@ -415,7 +416,7 @@ Object getValue(JDBCType jdbcType, int getInt(TDSReader tdsReader) throws SQLServerException { Integer value = (Integer) getValue(JDBCType.INTEGER, null, null, tdsReader); - return null != value ? value.intValue() : 0; + return null != value ? value : 0; } /** @@ -494,8 +495,8 @@ private void setTypeDefinition(DTV dtv) { // - the specified input scale (if any) // - the registered output scale Integer inScale = dtv.getScale(); - if (null != inScale && scale < inScale.intValue()) - scale = inScale.intValue(); + if (null != inScale && scale < inScale) + scale = inScale; if (param.isOutput() && scale < param.getOutScale()) scale = param.getOutScale(); @@ -878,7 +879,10 @@ else if ((null != jdbcTypeSetByUser) && ((jdbcTypeSetByUser == JDBCType.NVARCHAR case GUID: param.typeDefinition = SSType.GUID.toString(); break; - + + case SQL_VARIANT: + param.typeDefinition = SSType.SQL_VARIANT.toString(); + break; default: assert false : "Unexpected JDBC type " + dtv.getJdbcType(); break; @@ -1133,6 +1137,17 @@ void execute(DTV dtv, setTypeDefinition(dtv); } + /* + * (non-Javadoc) + * + * @see com.microsoft.sqlserver.jdbc.DTVExecuteOp#execute(com.microsoft.sqlserver.jdbc.DTV, microsoft.sql.SqlVariant) + */ + @Override + void execute(DTV dtv, + SqlVariant SqlVariantValue) throws SQLServerException { + setTypeDefinition(dtv); + } + } /** diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java index 46f08e9c2c..7a47b6fecd 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java @@ -47,6 +47,24 @@ final class SQLCollation implements java.io.Serializable static final int tdsLength() { return 5; } // Length of collation in TDS (in bytes) + /** + * Returns the collation info + * + * @return + */ + int getCollationInfo() { + return this.info; + } + + /** + * return sort ID + * + * @return + */ + int getCollationSortID() { + return this.sortId; + } + /** * Reads TDS collation from TDS buffer into SQLCollation class. * @param tdsReader diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java index f31892ebfe..94d392034c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java @@ -10,7 +10,7 @@ final class SQLJdbcVersion { static final int major = 6; - static final int minor = 2; + static final int minor = 3; static final int patch = 0; static final int build = 0; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBlob.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBlob.java index 452455111f..ef74f5c488 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBlob.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBlob.java @@ -197,13 +197,13 @@ public byte[] getBytes(long pos, getBytesFromStream(); if (pos < 1) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPositionIndex")); - Object[] msgArgs = {new Long(pos)}; + Object[] msgArgs = {pos}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } if (length < 0) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidLength")); - Object[] msgArgs = {new Integer(length)}; + Object[] msgArgs = {length}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } @@ -271,7 +271,7 @@ public long position(Blob pattern, getBytesFromStream(); if (start < 1) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPositionIndex")); - Object[] msgArgs = {new Long(start)}; + Object[] msgArgs = {start}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } @@ -299,7 +299,7 @@ public long position(byte[] bPattern, getBytesFromStream(); if (start < 1) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPositionIndex")); - Object[] msgArgs = {new Long(start)}; + Object[] msgArgs = {start}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } @@ -345,7 +345,7 @@ public void truncate(long len) throws SQLException { if (len < 0) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidLength")); - Object[] msgArgs = {new Long(len)}; + Object[] msgArgs = {len}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } @@ -431,14 +431,14 @@ public int setBytes(long pos, // Offset must be within incoming bytes boundary. if (offset < 0 || offset > bytes.length) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidOffset")); - Object[] msgArgs = {new Integer(offset)}; + Object[] msgArgs = {offset}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } // len must be within incoming bytes boundary. if (len < 0 || len > bytes.length - offset) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidLength")); - Object[] msgArgs = {new Integer(len)}; + Object[] msgArgs = {len}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } @@ -447,7 +447,7 @@ public int setBytes(long pos, // past the end of data to request "append" mode. if (pos <= 0 || pos > value.length + 1) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPositionIndex")); - Object[] msgArgs = {new Long(pos)}; + Object[] msgArgs = {pos}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java index 2fa3a3ef07..3076c90177 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java @@ -1136,7 +1136,10 @@ private void writeTypeInfo(TDSWriter tdsWriter, tdsWriter.writeByte((byte) srcScale); } break; - + case microsoft.sql.Types.SQL_VARIANT: //0x62 + tdsWriter.writeByte(TDSType.SQL_VARIANT.byteValue()); + tdsWriter.writeInt(TDS.SQL_VARIANT_LENGTH); + break; default: MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_BulkTypeNotSupported")); String unsupportedDataType = JDBCType.of(srcJdbcType).toString().toLowerCase(Locale.ENGLISH); @@ -1455,7 +1458,8 @@ private String getDestTypeFromSrcType(int srcColIndx, else { return "datetimeoffset(" + bulkScale + ")"; } - + case microsoft.sql.Types.SQL_VARIANT: + return "sql_variant"; default: { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_BulkTypeNotSupported")); Object[] msgArgs = {JDBCType.of(bulkJdbcType).toString().toLowerCase(Locale.ENGLISH)}; @@ -1483,7 +1487,7 @@ private String createInsertBulkCommand(TDSWriter tdsWriter) throws SQLServerExce .toUpperCase(Locale.ENGLISH); if (null != columnCollation && columnCollation.trim().length() > 0) { // we are adding collate in command only for char and varchar - if (null != destType && (destType.toLowerCase().trim().startsWith("char") || destType.toLowerCase().trim().startsWith("varchar"))) + if (null != destType && (destType.toLowerCase(Locale.ENGLISH).trim().startsWith("char") || destType.toLowerCase(Locale.ENGLISH).trim().startsWith("varchar"))) addCollate = " COLLATE " + columnCollation; } bulkCmd.append("[" + colMapping.destinationColumnName + "] " + destType + addCollate + endColumn); @@ -2045,6 +2049,9 @@ private void writeNullToTdsWriter(TDSWriter tdsWriter, case microsoft.sql.Types.DATETIMEOFFSET: tdsWriter.writeByte((byte) 0x00); return; + case microsoft.sql.Types.SQL_VARIANT: + tdsWriter.writeInt((byte) 0x00); + return; default: MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_BulkTypeNotSupported")); Object[] msgArgs = {JDBCType.of(srcJdbcType).toString().toLowerCase(Locale.ENGLISH)}; @@ -2141,7 +2148,7 @@ else if (null != sourceBulkRecord) { if (bulkNullable) { tdsWriter.writeByte((byte) 0x01); } - tdsWriter.writeByte((byte) (((Boolean) colValue).booleanValue() ? 1 : 0)); + tdsWriter.writeByte((byte) ((Boolean) colValue ? 1 : 0)); } break; @@ -2510,11 +2517,19 @@ else if (4 >= bulkScale) tdsWriter.writeDateTimeOffset(colValue, bulkScale, destSSType); } break; - + case microsoft.sql.Types.SQL_VARIANT: + boolean isShiloh = (8 >= connection.getServerMajorVersion() ? true : false); + if (isShiloh) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_SQLVariantSupport")); + throw new SQLServerException(null, form.format(new Object[] {}), null, 0, false); + } + writeSqlVariant(tdsWriter, colValue, sourceResultSet, srcColOrdinal, destColOrdinal, bulkJdbcType, bulkScale, isStreaming); + break; default: MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_BulkTypeNotSupported")); Object[] msgArgs = {JDBCType.of(bulkJdbcType).toString().toLowerCase(Locale.ENGLISH)}; SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, true); + break; } // End of switch } catch (ClassCastException ex) { @@ -2528,6 +2543,279 @@ else if (4 >= bulkScale) throw new SQLServerException(form.format(msgArgs), SQLState.DATA_EXCEPTION_NOT_SPECIFIC, DriverError.NOT_SET, ex); } } + + /** + * Writes sql_variant data based on the baseType for bulkcopy + * + * @throws SQLServerException + */ + private void writeSqlVariant(TDSWriter tdsWriter, + Object colValue, + ResultSet sourceResultSet, + int srcColOrdinal, + int destColOrdinal, + int bulkJdbcType, + int bulkScale, + boolean isStreaming) throws SQLServerException { + if (null == colValue) { + writeNullToTdsWriter(tdsWriter, bulkJdbcType, isStreaming); + return; + } + SqlVariant variantType = ((SQLServerResultSet) sourceResultSet).getVariantInternalType(srcColOrdinal); + int baseType = variantType.getBaseType(); + // for sql variant we normally should return the colvalue for time as time string. but for + // bulkcopy we need it to be timestamp. so we have to retrieve it again once we are in bulkcopy + // and make sure that the base type is time. + if (TDSType.TIMEN == TDSType.valueOf(baseType)) { + variantType.setIsBaseTypeTimeValue(true); + ((SQLServerResultSet) sourceResultSet).setInternalVariantType(srcColOrdinal, variantType); + colValue = ((SQLServerResultSet) sourceResultSet).getObject(srcColOrdinal); + } + switch (TDSType.valueOf(baseType)) { + case INT8: + writeBulkCopySqlVariantHeader(10, TDSType.INT8.byteValue(), (byte) 0, tdsWriter); + tdsWriter.writeLong(Long.valueOf(colValue.toString())); + break; + + case INT4: + writeBulkCopySqlVariantHeader(6, TDSType.INT4.byteValue(), (byte) 0, tdsWriter); + tdsWriter.writeInt(Integer.valueOf(colValue.toString())); + break; + + case INT2: + writeBulkCopySqlVariantHeader(4, TDSType.INT2.byteValue(), (byte) 0, tdsWriter); + tdsWriter.writeShort(Short.valueOf(colValue.toString())); + break; + + case INT1: + writeBulkCopySqlVariantHeader(3, TDSType.INT1.byteValue(), (byte) 0, tdsWriter); + tdsWriter.writeByte(Byte.valueOf(colValue.toString())); + break; + + case FLOAT8: + writeBulkCopySqlVariantHeader(10, TDSType.FLOAT8.byteValue(), (byte) 0, tdsWriter); + tdsWriter.writeDouble(Double.valueOf(colValue.toString())); + break; + + case FLOAT4: + writeBulkCopySqlVariantHeader(6, TDSType.FLOAT4.byteValue(), (byte) 0, tdsWriter); + tdsWriter.writeReal(Float.valueOf(colValue.toString())); + break; + + case MONEY8: + // For decimalN we right TDSWriter.BIGDECIMAL_MAX_LENGTH as maximum length = 17 + // 17 + 2 for basetype and probBytes + 2 for precision and length = 21 the length of data in header + writeBulkCopySqlVariantHeader(21, TDSType.DECIMALN.byteValue(), (byte) 2, tdsWriter); + tdsWriter.writeByte((byte) 38); + tdsWriter.writeByte((byte) 4); + tdsWriter.writeSqlVariantInternalBigDecimal((BigDecimal) colValue, bulkJdbcType); + break; + + case MONEY4: + writeBulkCopySqlVariantHeader(21, TDSType.DECIMALN.byteValue(), (byte) 2, tdsWriter); + tdsWriter.writeByte((byte) 38); + tdsWriter.writeByte((byte) 4); + tdsWriter.writeSqlVariantInternalBigDecimal((BigDecimal) colValue, bulkJdbcType); + break; + + case BIT1: + writeBulkCopySqlVariantHeader(3, TDSType.BIT1.byteValue(), (byte) 0, tdsWriter); + tdsWriter.writeByte((byte) (((Boolean) colValue).booleanValue() ? 1 : 0)); + break; + + case DATEN: + writeBulkCopySqlVariantHeader(5, TDSType.DATEN.byteValue(), (byte) 0, tdsWriter); + tdsWriter.writeDate(colValue.toString()); + break; + + case TIMEN: + bulkScale = variantType.getScale(); + int timeHeaderLength = 0x08; // default + if (2 >= bulkScale) { + timeHeaderLength = 0x06; + } + else if (4 >= bulkScale) { + timeHeaderLength = 0x07; + } + else { + timeHeaderLength = 0x08; + } + writeBulkCopySqlVariantHeader(timeHeaderLength, TDSType.TIMEN.byteValue(), (byte) 1, tdsWriter); // depending on scale, the header + // length + // defers + tdsWriter.writeByte((byte) bulkScale); + tdsWriter.writeTime((java.sql.Timestamp) colValue, bulkScale); + break; + + case DATETIME8: + writeBulkCopySqlVariantHeader(10, TDSType.DATETIME8.byteValue(), (byte) 0, tdsWriter); + tdsWriter.writeDatetime(colValue.toString()); + break; + + case DATETIME4: + // when the type is ambiguous, we write to bigger type + writeBulkCopySqlVariantHeader(10, TDSType.DATETIME8.byteValue(), (byte) 0, tdsWriter); + tdsWriter.writeDatetime(colValue.toString()); + break; + + case DATETIME2N: + writeBulkCopySqlVariantHeader(10, TDSType.DATETIME2N.byteValue(), (byte) 1, tdsWriter); // 1 is probbytes for time + tdsWriter.writeByte((byte) 0x03); + String timeStampValue = colValue.toString(); + tdsWriter.writeTime(java.sql.Timestamp.valueOf(timeStampValue), 0x03); // datetime2 in sql_variant has up to scale 3 support + // Send only the date part + tdsWriter.writeDate(timeStampValue.substring(0, timeStampValue.lastIndexOf(' '))); + break; + + case BIGCHAR: + int length = colValue.toString().length(); + writeBulkCopySqlVariantHeader(9 + length, TDSType.BIGCHAR.byteValue(), (byte) 7, tdsWriter); + tdsWriter.writeCollationForSqlVariant(variantType); // writes collation info and sortID + tdsWriter.writeShort((short) (length)); + SQLCollation destCollation = destColumnMetadata.get(destColOrdinal).collation; + if (null != destCollation) { + tdsWriter.writeBytes(colValue.toString().getBytes(destColumnMetadata.get(destColOrdinal).collation.getCharset())); + } + else { + tdsWriter.writeBytes(colValue.toString().getBytes()); + } + break; + + case BIGVARCHAR: + length = colValue.toString().length(); + writeBulkCopySqlVariantHeader(9 + length, TDSType.BIGVARCHAR.byteValue(), (byte) 7, tdsWriter); + tdsWriter.writeCollationForSqlVariant(variantType); // writes collation info and sortID + tdsWriter.writeShort((short) (length)); + + destCollation = destColumnMetadata.get(destColOrdinal).collation; + if (null != destCollation) { + tdsWriter.writeBytes(colValue.toString().getBytes(destColumnMetadata.get(destColOrdinal).collation.getCharset())); + } + else { + tdsWriter.writeBytes(colValue.toString().getBytes()); + } + break; + + case NCHAR: + length = colValue.toString().length() * 2; + writeBulkCopySqlVariantHeader(9 + length, TDSType.NCHAR.byteValue(), (byte) 7, tdsWriter); + tdsWriter.writeCollationForSqlVariant(variantType); // writes collation info and sortID + int stringLength = colValue.toString().length(); + byte[] typevarlen = new byte[2]; + typevarlen[0] = (byte) (2 * stringLength & 0xFF); + typevarlen[1] = (byte) ((2 * stringLength >> 8) & 0xFF); + tdsWriter.writeBytes(typevarlen); + tdsWriter.writeString(colValue.toString()); + break; + + case NVARCHAR: + length = colValue.toString().length() * 2; + writeBulkCopySqlVariantHeader(9 + length, TDSType.NVARCHAR.byteValue(), (byte) 7, tdsWriter); + tdsWriter.writeCollationForSqlVariant(variantType); // writes collation info and sortID + stringLength = colValue.toString().length(); + typevarlen = new byte[2]; + typevarlen[0] = (byte) (2 * stringLength & 0xFF); + typevarlen[1] = (byte) ((2 * stringLength >> 8) & 0xFF); + tdsWriter.writeBytes(typevarlen); + tdsWriter.writeString(colValue.toString()); + break; + + case GUID: + length = colValue.toString().length(); + writeBulkCopySqlVariantHeader(9 + length, TDSType.BIGCHAR.byteValue(), (byte) 7, tdsWriter); + // since while reading collation from sourceMetaData in guid we don't read collation, cause we are reading binary + // but in writing it we are using char, we need to get the collation. + SQLCollation collation = (null != destColumnMetadata.get(srcColOrdinal).collation) ? destColumnMetadata.get(srcColOrdinal).collation + : connection.getDatabaseCollation(); + variantType.setCollation(collation); + tdsWriter.writeCollationForSqlVariant(variantType); // writes collation info and sortID + tdsWriter.writeShort((short) (length)); + // converting string into destination collation using Charset + destCollation = destColumnMetadata.get(destColOrdinal).collation; + if (null != destCollation) { + tdsWriter.writeBytes(colValue.toString().getBytes(destColumnMetadata.get(destColOrdinal).collation.getCharset())); + } + else { + tdsWriter.writeBytes(colValue.toString().getBytes()); + } + break; + + case BIGBINARY: + byte[] b = (byte[]) colValue; + length = b.length; + writeBulkCopySqlVariantHeader(4 + length, TDSType.BIGVARBINARY.byteValue(), (byte) 2, tdsWriter); + tdsWriter.writeShort((short) (variantType.getMaxLength())); // length + if (null == colValue) { + writeNullToTdsWriter(tdsWriter, bulkJdbcType, isStreaming); + } + else { + byte[] srcBytes; + if (colValue instanceof byte[]) { + srcBytes = (byte[]) colValue; + } + else { + try { + srcBytes = ParameterUtils.HexToBin(colValue.toString()); + } + catch (SQLServerException e) { + throw new SQLServerException(SQLServerException.getErrString("R_unableRetrieveSourceData"), e); + } + } + tdsWriter.writeBytes(srcBytes); + } + break; + + case BIGVARBINARY: + b = (byte[]) colValue; + length = b.length; + writeBulkCopySqlVariantHeader(4 + length, TDSType.BIGVARBINARY.byteValue(), (byte) 2, tdsWriter); + tdsWriter.writeShort((short) (variantType.getMaxLength())); // length + if (null == colValue) { + writeNullToTdsWriter(tdsWriter, bulkJdbcType, isStreaming); + } + else { + byte[] srcBytes; + if (colValue instanceof byte[]) { + srcBytes = (byte[]) colValue; + } + else { + try { + srcBytes = ParameterUtils.HexToBin(colValue.toString()); + } + catch (SQLServerException e) { + throw new SQLServerException(SQLServerException.getErrString("R_unableRetrieveSourceData"), e); + } + } + tdsWriter.writeBytes(srcBytes); + } + break; + + default: + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_BulkTypeNotSupported")); + Object[] msgArgs = {JDBCType.of(bulkJdbcType).toString().toLowerCase(Locale.ENGLISH)}; + SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, true); + break; + } + } + + /** + * Write header for sql_variant + * + * @param length: + * length of base type + Basetype + probBytes + * @param tdsType + * @param probBytes + * @param tdsWriter + * @throws SQLServerException + */ + private void writeBulkCopySqlVariantHeader(int length, + byte tdsType, + byte probBytes, + TDSWriter tdsWriter) throws SQLServerException { + tdsWriter.writeInt(length); + tdsWriter.writeByte(tdsType); + tdsWriter.writeByte(probBytes); + } private Object readColumnFromResultSet(int srcColOrdinal, int srcJdbcType, @@ -2630,14 +2918,16 @@ private Object readColumnFromResultSet(int srcColOrdinal, // We can safely cast the result set to a SQLServerResultSet as the DatetimeOffset type is only available in the JDBC driver. return ((SQLServerResultSet) sourceResultSet).getDateTimeOffset(srcColOrdinal); + case microsoft.sql.Types.SQL_VARIANT: + return sourceResultSet.getObject(srcColOrdinal); default: MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_BulkTypeNotSupported")); Object[] msgArgs = {JDBCType.of(srcJdbcType).toString().toLowerCase(Locale.ENGLISH)}; SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, true); // This return will never be executed, but it is needed as Eclipse complains otherwise. return null; - } // End of switch - }// End of Try + } + } catch (SQLException e) { throw new SQLServerException(SQLServerException.getErrString("R_unableRetrieveSourceData"), e); } @@ -3030,57 +3320,57 @@ private byte[] normalizedValue(JDBCType destJdbcType, try { switch (destJdbcType) { case BIT: - longValue = Long.valueOf((Boolean) value ? 1 : 0); - return ByteBuffer.allocate(Long.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN).putLong(longValue.longValue()).array(); + longValue = (long) ((Boolean) value ? 1 : 0); + return ByteBuffer.allocate(Long.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN).putLong(longValue).array(); case TINYINT: case SMALLINT: switch (srcJdbcType) { case BIT: - longValue = new Long((Boolean) value ? 1 : 0); + longValue = (long) ((Boolean) value ? 1 : 0); break; default: if (value instanceof Integer) { int intValue = (int) value; short shortValue = (short) intValue; - longValue = new Long(shortValue); + longValue = (long) shortValue; } else - longValue = new Long((short) value); + longValue = (long) (short) value; } - return ByteBuffer.allocate(Long.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN).putLong(longValue.longValue()).array(); + return ByteBuffer.allocate(Long.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN).putLong(longValue).array(); case INTEGER: switch (srcJdbcType) { case BIT: - longValue = new Long((Boolean) value ? 1 : 0); + longValue = (long) ((Boolean) value ? 1 : 0); break; case TINYINT: case SMALLINT: - longValue = new Long((short) value); + longValue = (long) (short) value; break; default: longValue = new Long((Integer) value); } - return ByteBuffer.allocate(Long.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN).putLong(longValue.longValue()).array(); + return ByteBuffer.allocate(Long.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN).putLong(longValue).array(); case BIGINT: switch (srcJdbcType) { case BIT: - longValue = new Long((Boolean) value ? 1 : 0); + longValue = (long) ((Boolean) value ? 1 : 0); break; case TINYINT: case SMALLINT: - longValue = new Long((short) value); + longValue = (long) (short) value; break; case INTEGER: longValue = new Long((Integer) value); break; default: - longValue = new Long((long) value); + longValue = (long) value; } - return ByteBuffer.allocate(Long.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN).putLong(longValue.longValue()).array(); + return ByteBuffer.allocate(Long.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN).putLong(longValue).array(); case BINARY: case VARBINARY: diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java index 2dd460cc38..e5eaa7ccf5 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java @@ -30,6 +30,7 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Calendar; +import java.util.UUID; /** * CallableStatement implements JDBC callable statements. CallableStatement allows the caller to specify the procedure name to call along with input @@ -89,11 +90,11 @@ String getClassNameInternal() { public void registerOutParameter(int index, int sqlType) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {new Integer(index), new Integer(sqlType)}); + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {index, sqlType}); checkClosed(); if (index < 1 || index > inOutParam.length) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_indexOutOfRange")); - Object[] msgArgs = {new Integer(index)}; + Object[] msgArgs = {index}; SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), "7009", false); } @@ -318,7 +319,7 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException { // If we were asked to retain the OUT parameters as we skip past them, // then report an error if we did not find any. MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_valueNotSetForParameter")); - Object[] msgArgs = {new Integer(outParamIndex + 1)}; + Object[] msgArgs = {outParamIndex + 1}; SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), null, false); } @@ -346,7 +347,7 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException { int sqlType, String typeName) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {new Integer(index), new Integer(sqlType), typeName}); + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {index, sqlType, typeName}); checkClosed(); @@ -360,7 +361,7 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException { int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "registerOutParameter", - new Object[] {new Integer(index), new Integer(sqlType), new Integer(scale)}); + new Object[] {index, sqlType, scale}); checkClosed(); @@ -376,7 +377,7 @@ public void registerOutParameter(int index, int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "registerOutParameter", - new Object[] {new Integer(index), new Integer(sqlType), new Integer(scale), new Integer(precision)}); + new Object[] {index, sqlType, scale, precision}); checkClosed(); @@ -395,14 +396,14 @@ private Parameter getterGetParam(int index) throws SQLServerException { // Check for valid index if (index < 1 || index > inOutParam.length) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidOutputParameter")); - Object[] msgArgs = {new Integer(index)}; + Object[] msgArgs = {index}; SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), "07009", false); } // Check index refers to a registered OUT parameter if (!inOutParam[index - 1].isOutput()) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_outputParameterNotRegisteredForOutput")); - Object[] msgArgs = {new Integer(index)}; + Object[] msgArgs = {index}; SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), "07009", true); } @@ -457,7 +458,7 @@ public int getInt(int index) throws SQLServerException { checkClosed(); Integer value = (Integer) getValue(index, JDBCType.INTEGER); loggerExternal.exiting(getClassNameLogging(), "getInt", value); - return null != value ? value.intValue() : 0; + return null != value ? value : 0; } public int getInt(String sCol) throws SQLServerException { @@ -465,21 +466,29 @@ public int getInt(String sCol) throws SQLServerException { checkClosed(); Integer value = (Integer) getValue(findColumn(sCol), JDBCType.INTEGER); loggerExternal.exiting(getClassNameLogging(), "getInt", value); - return null != value ? value.intValue() : 0; + return null != value ? value : 0; } public String getString(int index) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getString", index); checkClosed(); - String value = (String) getValue(index, JDBCType.CHAR); + String value = null; + Object objectValue = getValue(index, JDBCType.CHAR); + if (null != objectValue) { + value = objectValue.toString(); + } loggerExternal.exiting(getClassNameLogging(), "getString", value); return value; } - + public String getString(String sCol) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getString", sCol); checkClosed(); - String value = (String) getValue(findColumn(sCol), JDBCType.CHAR); + String value = null; + Object objectValue = getValue(findColumn(sCol), JDBCType.CHAR); + if (null != objectValue) { + value = objectValue.toString(); + } loggerExternal.exiting(getClassNameLogging(), "getString", value); return value; } @@ -504,7 +513,7 @@ public final String getNString(String parameterName) throws SQLException { public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "getBigDecimal", new Object[] {Integer.valueOf(parameterIndex), Integer.valueOf(scale)}); + loggerExternal.entering(getClassNameLogging(), "getBigDecimal", new Object[] {parameterIndex, scale}); checkClosed(); BigDecimal value = (BigDecimal) getValue(parameterIndex, JDBCType.DECIMAL); if (null != value) @@ -517,7 +526,7 @@ public BigDecimal getBigDecimal(int parameterIndex, public BigDecimal getBigDecimal(String parameterName, int scale) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "getBigDecimal", new Object[] {parameterName, Integer.valueOf(scale)}); + loggerExternal.entering(getClassNameLogging(), "getBigDecimal", new Object[] {parameterName, scale}); checkClosed(); BigDecimal value = (BigDecimal) getValue(findColumn(parameterName), JDBCType.DECIMAL); if (null != value) @@ -531,7 +540,7 @@ public boolean getBoolean(int index) throws SQLServerException { checkClosed(); Boolean value = (Boolean) getValue(index, JDBCType.BIT); loggerExternal.exiting(getClassNameLogging(), "getBoolean", value); - return null != value ? value.booleanValue() : false; + return null != value ? value : false; } public boolean getBoolean(String sCol) throws SQLServerException { @@ -539,7 +548,7 @@ public boolean getBoolean(String sCol) throws SQLServerException { checkClosed(); Boolean value = (Boolean) getValue(findColumn(sCol), JDBCType.BIT); loggerExternal.exiting(getClassNameLogging(), "getBoolean", value); - return null != value ? value.booleanValue() : false; + return null != value ? value : false; } public byte getByte(int index) throws SQLServerException { @@ -617,7 +626,7 @@ public double getDouble(int index) throws SQLServerException { checkClosed(); Double value = (Double) getValue(index, JDBCType.DOUBLE); loggerExternal.exiting(getClassNameLogging(), "getDouble", value); - return null != value ? value.doubleValue() : 0; + return null != value ? value : 0; } public double getDouble(String sCol) throws SQLServerException { @@ -625,7 +634,7 @@ public double getDouble(String sCol) throws SQLServerException { checkClosed(); Double value = (Double) getValue(findColumn(sCol), JDBCType.DOUBLE); loggerExternal.exiting(getClassNameLogging(), "getDouble", value); - return null != value ? value.doubleValue() : 0; + return null != value ? value : 0; } public float getFloat(int index) throws SQLServerException { @@ -633,7 +642,7 @@ public float getFloat(int index) throws SQLServerException { checkClosed(); Float value = (Float) getValue(index, JDBCType.REAL); loggerExternal.exiting(getClassNameLogging(), "getFloat", value); - return null != value ? value.floatValue() : 0; + return null != value ? value : 0; } public float getFloat(String sCol) throws SQLServerException { @@ -642,7 +651,7 @@ public float getFloat(String sCol) throws SQLServerException { checkClosed(); Float value = (Float) getValue(findColumn(sCol), JDBCType.REAL); loggerExternal.exiting(getClassNameLogging(), "getFloat", value); - return null != value ? value.floatValue() : 0; + return null != value ? value : 0; } public long getLong(int index) throws SQLServerException { @@ -651,7 +660,7 @@ public long getLong(int index) throws SQLServerException { checkClosed(); Long value = (Long) getValue(index, JDBCType.BIGINT); loggerExternal.exiting(getClassNameLogging(), "getLong", value); - return null != value ? value.longValue() : 0; + return null != value ? value : 0; } public long getLong(String sCol) throws SQLServerException { @@ -659,7 +668,7 @@ public long getLong(String sCol) throws SQLServerException { checkClosed(); Long value = (Long) getValue(findColumn(sCol), JDBCType.BIGINT); loggerExternal.exiting(getClassNameLogging(), "getLong", value); - return null != value ? value.longValue() : 0; + return null != value ? value : 0; } public Object getObject(int index) throws SQLServerException { @@ -674,8 +683,84 @@ public Object getObject(int index) throws SQLServerException { public T getObject(int index, Class type) throws SQLException { - // The driver currently does not implement the optional JDBC APIs - throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + loggerExternal.entering(getClassNameLogging(), "getObject", index); + checkClosed(); + Object returnValue; + if (type == String.class) { + returnValue = getString(index); + } + else if (type == Byte.class) { + byte byteValue = getByte(index); + returnValue = wasNull() ? null : byteValue; + } + else if (type == Short.class) { + short shortValue = getShort(index); + returnValue = wasNull() ? null : shortValue; + } + else if (type == Integer.class) { + int intValue = getInt(index); + returnValue = wasNull() ? null : intValue; + } + else if (type == Long.class) { + long longValue = getLong(index); + returnValue = wasNull() ? null : longValue; + } + else if (type == BigDecimal.class) { + returnValue = getBigDecimal(index); + } + else if (type == Boolean.class) { + boolean booleanValue = getBoolean(index); + returnValue = wasNull() ? null : booleanValue; + } + else if (type == java.sql.Date.class) { + returnValue = getDate(index); + } + else if (type == java.sql.Time.class) { + returnValue = getTime(index); + } + else if (type == java.sql.Timestamp.class) { + returnValue = getTimestamp(index); + } + else if (type == microsoft.sql.DateTimeOffset.class) { + returnValue = getDateTimeOffset(index); + } + else if (type == UUID.class) { + // read binary, avoid string allocation and parsing + byte[] guid = getBytes(index); + returnValue = guid != null ? Util.readGUIDtoUUID(guid) : null; + } + else if (type == SQLXML.class) { + returnValue = getSQLXML(index); + } + else if (type == Blob.class) { + returnValue = getBlob(index); + } + else if (type == Clob.class) { + returnValue = getClob(index); + } + else if (type == NClob.class) { + returnValue = getNClob(index); + } + else if (type == byte[].class) { + returnValue = getBytes(index); + } + else if (type == Float.class) { + float floatValue = getFloat(index); + returnValue = wasNull() ? null : floatValue; + } + else if (type == Double.class) { + double doubleValue = getDouble(index); + returnValue = wasNull() ? null : doubleValue; + } + else { + // if the type is not supported the specification says the should + // a SQLException instead of SQLFeatureNotSupportedException + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unsupportedConversionTo")); + Object[] msgArgs = {type}; + throw new SQLServerException(form.format(msgArgs), SQLState.DATA_EXCEPTION_NOT_SPECIFIC, DriverError.NOT_SET, null); + } + loggerExternal.exiting(getClassNameLogging(), "getObject", index); + return type.cast(returnValue); } public Object getObject(String sCol) throws SQLServerException { @@ -690,8 +775,12 @@ public Object getObject(String sCol) throws SQLServerException { public T getObject(String sCol, Class type) throws SQLException { - // The driver currently does not implement the optional JDBC APIs - throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + loggerExternal.entering(getClassNameLogging(), "getObject", sCol); + checkClosed(); + int parameterIndex = findColumn(sCol); + T value = getObject(parameterIndex, type); + loggerExternal.exiting(getClassNameLogging(), "getObject", value); + return value; } public short getShort(int index) throws SQLServerException { @@ -700,7 +789,7 @@ public short getShort(int index) throws SQLServerException { checkClosed(); Short value = (Short) getValue(index, JDBCType.SMALLINT); loggerExternal.exiting(getClassNameLogging(), "getShort", value); - return null != value ? value.shortValue() : 0; + return null != value ? value : 0; } public short getShort(String sCol) throws SQLServerException { @@ -708,7 +797,7 @@ public short getShort(String sCol) throws SQLServerException { checkClosed(); Short value = (Short) getValue(findColumn(sCol), JDBCType.SMALLINT); loggerExternal.exiting(getClassNameLogging(), "getShort", value); - return null != value ? value.shortValue() : 0; + return null != value ? value : 0; } public Time getTime(int index) throws SQLServerException { @@ -1752,7 +1841,7 @@ public void setObject(String sCol, // For all other types, this value will be ignored. setObject(setterGetParam(findColumn(sCol)), o, JavaType.of(o), JDBCType.of(n), - (java.sql.Types.NUMERIC == n || java.sql.Types.DECIMAL == n) ? Integer.valueOf(m) : null, null, forceEncrypt, findColumn(sCol), null); + (java.sql.Types.NUMERIC == n || java.sql.Types.DECIMAL == n) ? m : null, null, forceEncrypt, findColumn(sCol), null); loggerExternal.exiting(getClassNameLogging(), "setObject"); } @@ -1802,7 +1891,7 @@ public final void setObject(String sCol, setObject(setterGetParam(findColumn(sCol)), x, JavaType.of(x), JDBCType.of(targetSqlType), (java.sql.Types.NUMERIC == targetSqlType || java.sql.Types.DECIMAL == targetSqlType - || InputStream.class.isInstance(x) || Reader.class.isInstance(x)) ? Integer.valueOf(scale) : null, + || InputStream.class.isInstance(x) || Reader.class.isInstance(x)) ? scale : null, precision, false, findColumn(sCol), null); loggerExternal.exiting(getClassNameLogging(), "setObject"); @@ -2273,7 +2362,7 @@ public void setByte(String sCol, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setByte", new Object[] {sCol, b}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TINYINT, Byte.valueOf(b), JavaType.BYTE, false); + setValue(findColumn(sCol), JDBCType.TINYINT, b, JavaType.BYTE, false); loggerExternal.exiting(getClassNameLogging(), "setByte"); } @@ -2299,7 +2388,7 @@ public void setByte(String sCol, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setByte", new Object[] {sCol, b, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.TINYINT, Byte.valueOf(b), JavaType.BYTE, forceEncrypt); + setValue(findColumn(sCol), JDBCType.TINYINT, b, JavaType.BYTE, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setByte"); } @@ -2506,7 +2595,7 @@ public void setDouble(String sCol, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDouble", new Object[] {sCol, d}); checkClosed(); - setValue(findColumn(sCol), JDBCType.DOUBLE, Double.valueOf(d), JavaType.DOUBLE, false); + setValue(findColumn(sCol), JDBCType.DOUBLE, d, JavaType.DOUBLE, false); loggerExternal.exiting(getClassNameLogging(), "setDouble"); } @@ -2532,7 +2621,7 @@ public void setDouble(String sCol, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDouble", new Object[] {sCol, d, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.DOUBLE, Double.valueOf(d), JavaType.DOUBLE, forceEncrypt); + setValue(findColumn(sCol), JDBCType.DOUBLE, d, JavaType.DOUBLE, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setDouble"); } @@ -2541,7 +2630,7 @@ public void setFloat(String sCol, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setFloat", new Object[] {sCol, f}); checkClosed(); - setValue(findColumn(sCol), JDBCType.REAL, Float.valueOf(f), JavaType.FLOAT, false); + setValue(findColumn(sCol), JDBCType.REAL, f, JavaType.FLOAT, false); loggerExternal.exiting(getClassNameLogging(), "setFloat"); } @@ -2567,7 +2656,7 @@ public void setFloat(String sCol, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setFloat", new Object[] {sCol, f, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.REAL, Float.valueOf(f), JavaType.FLOAT, forceEncrypt); + setValue(findColumn(sCol), JDBCType.REAL, f, JavaType.FLOAT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setFloat"); } @@ -2576,7 +2665,7 @@ public void setInt(String sCol, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setInt", new Object[] {sCol, i}); checkClosed(); - setValue(findColumn(sCol), JDBCType.INTEGER, Integer.valueOf(i), JavaType.INTEGER, false); + setValue(findColumn(sCol), JDBCType.INTEGER, i, JavaType.INTEGER, false); loggerExternal.exiting(getClassNameLogging(), "setInt"); } @@ -2602,7 +2691,7 @@ public void setInt(String sCol, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setInt", new Object[] {sCol, i, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.INTEGER, Integer.valueOf(i), JavaType.INTEGER, forceEncrypt); + setValue(findColumn(sCol), JDBCType.INTEGER, i, JavaType.INTEGER, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setInt"); } @@ -2611,7 +2700,7 @@ public void setLong(String sCol, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setLong", new Object[] {sCol, l}); checkClosed(); - setValue(findColumn(sCol), JDBCType.BIGINT, Long.valueOf(l), JavaType.LONG, false); + setValue(findColumn(sCol), JDBCType.BIGINT, l, JavaType.LONG, false); loggerExternal.exiting(getClassNameLogging(), "setLong"); } @@ -2637,7 +2726,7 @@ public void setLong(String sCol, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setLong", new Object[] {sCol, l, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.BIGINT, Long.valueOf(l), JavaType.LONG, forceEncrypt); + setValue(findColumn(sCol), JDBCType.BIGINT, l, JavaType.LONG, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setLong"); } @@ -2646,7 +2735,7 @@ public void setShort(String sCol, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setShort", new Object[] {sCol, s}); checkClosed(); - setValue(findColumn(sCol), JDBCType.SMALLINT, Short.valueOf(s), JavaType.SHORT, false); + setValue(findColumn(sCol), JDBCType.SMALLINT, s, JavaType.SHORT, false); loggerExternal.exiting(getClassNameLogging(), "setShort"); } @@ -2672,7 +2761,7 @@ public void setShort(String sCol, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setShort", new Object[] {sCol, s, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.SMALLINT, Short.valueOf(s), JavaType.SHORT, forceEncrypt); + setValue(findColumn(sCol), JDBCType.SMALLINT, s, JavaType.SHORT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setShort"); } @@ -2681,7 +2770,7 @@ public void setBoolean(String sCol, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBoolean", new Object[] {sCol, b}); checkClosed(); - setValue(findColumn(sCol), JDBCType.BIT, Boolean.valueOf(b), JavaType.BOOLEAN, false); + setValue(findColumn(sCol), JDBCType.BIT, b, JavaType.BOOLEAN, false); loggerExternal.exiting(getClassNameLogging(), "setBoolean"); } @@ -2707,7 +2796,7 @@ public void setBoolean(String sCol, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBoolean", new Object[] {sCol, b, forceEncrypt}); checkClosed(); - setValue(findColumn(sCol), JDBCType.BIT, Boolean.valueOf(b), JavaType.BOOLEAN, forceEncrypt); + setValue(findColumn(sCol), JDBCType.BIT, b, JavaType.BOOLEAN, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setBoolean"); } @@ -2866,7 +2955,7 @@ public void registerOutParameter(String s, int n, String s1) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {s, new Integer(n), s1}); + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {s, n, s1}); checkClosed(); registerOutParameter(findColumn(s), n, s1); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); @@ -2877,7 +2966,7 @@ public void registerOutParameter(String parameterName, int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "registerOutParameter", - new Object[] {parameterName, new Integer(sqlType), new Integer(scale)}); + new Object[] {parameterName, sqlType, scale}); checkClosed(); registerOutParameter(findColumn(parameterName), sqlType, scale); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); @@ -2889,7 +2978,7 @@ public void registerOutParameter(String parameterName, int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "registerOutParameter", - new Object[] {parameterName, new Integer(sqlType), new Integer(scale)}); + new Object[] {parameterName, sqlType, scale}); checkClosed(); registerOutParameter(findColumn(parameterName), sqlType, precision, scale); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); @@ -2898,7 +2987,7 @@ public void registerOutParameter(String parameterName, public void registerOutParameter(String s, int n) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {s, new Integer(n)}); + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {s, n}); checkClosed(); registerOutParameter(findColumn(s), n); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement42.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement42.java index ee73cc1768..cf5e1f2460 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement42.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement42.java @@ -34,10 +34,10 @@ public void registerOutParameter(int index, DriverJDBCVersion.checkSupportsJDBC42(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {new Integer(index), sqlType}); + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {index, sqlType}); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - registerOutParameter(index, sqlType.getVendorTypeNumber().intValue()); + registerOutParameter(index, sqlType.getVendorTypeNumber()); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } @@ -47,10 +47,10 @@ public void registerOutParameter(int index, DriverJDBCVersion.checkSupportsJDBC42(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {new Integer(index), sqlType, typeName}); + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {index, sqlType, typeName}); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - registerOutParameter(index, sqlType.getVendorTypeNumber().intValue(), typeName); + registerOutParameter(index, sqlType.getVendorTypeNumber(), typeName); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } @@ -61,10 +61,10 @@ public void registerOutParameter(int index, DriverJDBCVersion.checkSupportsJDBC42(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {new Integer(index), sqlType, new Integer(scale)}); + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {index, sqlType, scale}); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - registerOutParameter(index, sqlType.getVendorTypeNumber().intValue(), scale); + registerOutParameter(index, sqlType.getVendorTypeNumber(), scale); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } @@ -76,10 +76,10 @@ public void registerOutParameter(int index, DriverJDBCVersion.checkSupportsJDBC42(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {new Integer(index), sqlType, new Integer(scale)}); + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {index, sqlType, scale}); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - registerOutParameter(index, sqlType.getVendorTypeNumber().intValue(), precision, scale); + registerOutParameter(index, sqlType.getVendorTypeNumber(), precision, scale); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } @@ -93,7 +93,7 @@ public void setObject(String sCol, loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {sCol, obj, jdbcType}); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - setObject(sCol, obj, jdbcType.getVendorTypeNumber().intValue()); + setObject(sCol, obj, jdbcType.getVendorTypeNumber()); loggerExternal.exiting(getClassNameLogging(), "setObject"); } @@ -108,7 +108,7 @@ public void setObject(String sCol, loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {sCol, obj, jdbcType, scale}); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - setObject(sCol, obj, jdbcType.getVendorTypeNumber().intValue(), scale); + setObject(sCol, obj, jdbcType.getVendorTypeNumber(), scale); loggerExternal.exiting(getClassNameLogging(), "setObject"); } @@ -124,7 +124,7 @@ public void setObject(String sCol, loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {sCol, obj, jdbcType, scale, forceEncrypt}); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - setObject(sCol, obj, jdbcType.getVendorTypeNumber().intValue(), scale, forceEncrypt); + setObject(sCol, obj, jdbcType.getVendorTypeNumber(), scale, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setObject"); } @@ -138,7 +138,7 @@ public void registerOutParameter(String parameterName, loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType, typeName}); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - registerOutParameter(parameterName, sqlType.getVendorTypeNumber().intValue(), typeName); + registerOutParameter(parameterName, sqlType.getVendorTypeNumber(), typeName); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } @@ -149,10 +149,10 @@ public void registerOutParameter(String parameterName, DriverJDBCVersion.checkSupportsJDBC42(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType, new Integer(scale)}); + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType, scale}); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - registerOutParameter(parameterName, sqlType.getVendorTypeNumber().intValue(), scale); + registerOutParameter(parameterName, sqlType.getVendorTypeNumber(), scale); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } @@ -164,10 +164,10 @@ public void registerOutParameter(String parameterName, DriverJDBCVersion.checkSupportsJDBC42(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType, new Integer(scale)}); + loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType, scale}); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - registerOutParameter(parameterName, sqlType.getVendorTypeNumber().intValue(), precision, scale); + registerOutParameter(parameterName, sqlType.getVendorTypeNumber(), precision, scale); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } @@ -180,7 +180,7 @@ public void registerOutParameter(String parameterName, loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType}); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - registerOutParameter(parameterName, sqlType.getVendorTypeNumber().intValue()); + registerOutParameter(parameterName, sqlType.getVendorTypeNumber()); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerClob.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerClob.java index 3accc9e204..f933727cfe 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerClob.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerClob.java @@ -275,13 +275,13 @@ public String getSubString(long pos, getStringFromStream(); if (pos < 1) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPositionIndex")); - Object[] msgArgs = {new Long(pos)}; + Object[] msgArgs = {pos}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } if (length < 0) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidLength")); - Object[] msgArgs = {new Integer(length)}; + Object[] msgArgs = {length}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } @@ -350,7 +350,7 @@ public long position(Clob searchstr, getStringFromStream(); if (start < 1) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPositionIndex")); - Object[] msgArgs = {new Long(start)}; + Object[] msgArgs = {start}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } @@ -379,7 +379,7 @@ public long position(String searchstr, getStringFromStream(); if (start < 1) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPositionIndex")); - Object[] msgArgs = {new Long(start)}; + Object[] msgArgs = {start}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } @@ -411,7 +411,7 @@ public void truncate(long len) throws SQLException { getStringFromStream(); if (len < 0) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidLength")); - Object[] msgArgs = {new Long(len)}; + Object[] msgArgs = {len}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } @@ -433,7 +433,7 @@ public java.io.OutputStream setAsciiStream(long pos) throws SQLException { if (pos < 1) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPositionIndex")); - Object[] msgArgs = {new Long(pos)}; + Object[] msgArgs = {pos}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } @@ -454,7 +454,7 @@ public java.io.Writer setCharacterStream(long pos) throws SQLException { if (pos < 1) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPositionIndex")); - Object[] msgArgs = {new Long(pos)}; + Object[] msgArgs = {pos}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } @@ -514,14 +514,14 @@ public int setString(long pos, // Offset must be within incoming string str boundary. if (offset < 0 || offset > str.length()) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidOffset")); - Object[] msgArgs = {new Integer(offset)}; + Object[] msgArgs = {offset}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } // len must be within incoming string str boundary. if (len < 0 || len > str.length() - offset) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidLength")); - Object[] msgArgs = {new Integer(len)}; + Object[] msgArgs = {len}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } @@ -530,7 +530,7 @@ public int setString(long pos, // past the end of data to request "append" mode. if (pos < 1 || pos > value.length() + 1) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPositionIndex")); - Object[] msgArgs = {new Long(pos)}; + Object[] msgArgs = {pos}; SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java index 9a1d89677a..c6e168a855 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java @@ -17,15 +17,13 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.MessageFormat; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; - -import org.apache.http.impl.client.HttpClientBuilder; +import java.util.Locale; import com.microsoft.azure.keyvault.KeyVaultClient; -import com.microsoft.azure.keyvault.KeyVaultClientImpl; import com.microsoft.azure.keyvault.models.KeyBundle; import com.microsoft.azure.keyvault.models.KeyOperationResult; +import com.microsoft.azure.keyvault.models.KeyVerifyResult; +import com.microsoft.azure.keyvault.webkey.JsonWebKeyEncryptionAlgorithm; import com.microsoft.azure.keyvault.webkey.JsonWebKeySignatureAlgorithm; /** @@ -66,26 +64,20 @@ public String getName() { } /** - * Constructor that takes a callback function to authenticate to AAD. This is used by KeyVaultClient at runtime to authenticate to Azure Key + * Constructor that authenticates to AAD. This is used by KeyVaultClient at runtime to authenticate to Azure Key * Vault. * - * @param authenticationCallback - * - Callback function used for authenticating to AAD. - * @param executorService - * - The ExecutorService used to create the keyVaultClient + * @param clientId + * Identifier of the client requesting the token. + * @param clientKey + * Key of the client requesting the token. * @throws SQLServerException * when an error occurs */ - public SQLServerColumnEncryptionAzureKeyVaultProvider(SQLServerKeyVaultAuthenticationCallback authenticationCallback, - ExecutorService executorService) throws SQLServerException { - if (null == authenticationCallback) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); - Object[] msgArgs1 = {"SQLServerKeyVaultAuthenticationCallback"}; - throw new SQLServerException(form.format(msgArgs1), null); - } - credential = new KeyVaultCredential(authenticationCallback); - HttpClientBuilder builder = HttpClientBuilder.create(); - keyVaultClient = new KeyVaultClientImpl(builder, executorService, credential); + public SQLServerColumnEncryptionAzureKeyVaultProvider(String clientId, + String clientKey) throws SQLServerException { + credential = new KeyVaultCredential(clientId, clientKey); + keyVaultClient = new KeyVaultClient(credential); } /** @@ -263,7 +255,7 @@ public byte[] encryptColumnEncryptionKey(String masterKeyPath, byte[] version = new byte[] {firstVersion[0]}; // Get the Unicode encoded bytes of cultureinvariant lower case masterKeyPath - byte[] masterKeyPathBytes = masterKeyPath.toLowerCase().getBytes(UTF_16LE); + byte[] masterKeyPathBytes = masterKeyPath.toLowerCase(Locale.ENGLISH).getBytes(UTF_16LE); byte[] keyPathLength = new byte[2]; keyPathLength[0] = (byte) (((short) masterKeyPathBytes.length) & 0xff); @@ -308,7 +300,7 @@ public byte[] encryptColumnEncryptionKey(String masterKeyPath, byte dataToSign[] = md.digest(); // Sign the hash - byte[] signedHash = AzureKeyVaultSignHashedData(dataToSign, masterKeyPath); + byte[] signedHash = AzureKeyVaultSignHashedData(dataToSign, masterKeyPath); if (signedHash.length != keySizeInBytes) { throw new SQLServerException(SQLServerException.getErrString("R_SignedHashLengthError"), null); @@ -405,7 +397,7 @@ private void ValidateNonEmptyAKVPath(String masterKeyPath) throws SQLServerExcep // A valid URI. // Check if it is pointing to AKV. - if (!parsedUri.getHost().toLowerCase().endsWith(azureKeyVaultDomainName)) { + if (!parsedUri.getHost().toLowerCase(Locale.ENGLISH).endsWith(azureKeyVaultDomainName)) { // Return an error indicating that the AKV url is invalid. MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_AKVMasterKeyPathInvalid")); Object[] msgArgs = {masterKeyPath}; @@ -433,14 +425,10 @@ private byte[] AzureKeyVaultWrap(String masterKeyPath, throw new SQLServerException(SQLServerException.getErrString("R_CEKNull"), null); } - KeyOperationResult wrappedKey = null; - try { - wrappedKey = keyVaultClient.wrapKeyAsync(masterKeyPath, encryptionAlgorithm, columnEncryptionKey).get(); - } - catch (InterruptedException | ExecutionException e) { - throw new SQLServerException(SQLServerException.getErrString("R_EncryptCEKError"), e); - } - return wrappedKey.getResult(); + JsonWebKeyEncryptionAlgorithm jsonEncryptionAlgorithm = new JsonWebKeyEncryptionAlgorithm(encryptionAlgorithm); + KeyOperationResult wrappedKey = keyVaultClient.wrapKey(masterKeyPath, jsonEncryptionAlgorithm, columnEncryptionKey); + + return wrappedKey.result(); } /** @@ -466,14 +454,10 @@ private byte[] AzureKeyVaultUnWrap(String masterKeyPath, throw new SQLServerException(SQLServerException.getErrString("R_EmptyEncryptedCEK"), null); } - KeyOperationResult unwrappedKey; - try { - unwrappedKey = keyVaultClient.unwrapKeyAsync(masterKeyPath, encryptionAlgorithm, encryptedColumnEncryptionKey).get(); - } - catch (InterruptedException | ExecutionException e) { - throw new SQLServerException(SQLServerException.getErrString("R_DecryptCEKError"), e); - } - return unwrappedKey.getResult(); + JsonWebKeyEncryptionAlgorithm jsonEncryptionAlgorithm = new JsonWebKeyEncryptionAlgorithm(encryptionAlgorithm); + KeyOperationResult unwrappedKey = keyVaultClient.unwrapKey(masterKeyPath, jsonEncryptionAlgorithm, encryptedColumnEncryptionKey); + + return unwrappedKey.result(); } /** @@ -490,14 +474,9 @@ private byte[] AzureKeyVaultSignHashedData(byte[] dataToSign, String masterKeyPath) throws SQLServerException { assert ((null != dataToSign) && (0 != dataToSign.length)); - KeyOperationResult signedData = null; - try { - signedData = keyVaultClient.signAsync(masterKeyPath, JsonWebKeySignatureAlgorithm.RS256, dataToSign).get(); - } - catch (InterruptedException | ExecutionException e) { - throw new SQLServerException(SQLServerException.getErrString("R_GenerateSignature"), e); - } - return signedData.getResult(); + KeyOperationResult signedData = keyVaultClient.sign(masterKeyPath, JsonWebKeySignatureAlgorithm.RS256, dataToSign); + + return signedData.result(); } /** @@ -516,15 +495,9 @@ private boolean AzureKeyVaultVerifySignature(byte[] dataToVerify, assert ((null != dataToVerify) && (0 != dataToVerify.length)); assert ((null != signature) && (0 != signature.length)); - boolean valid = false; - try { - valid = keyVaultClient.verifyAsync(masterKeyPath, JsonWebKeySignatureAlgorithm.RS256, dataToVerify, signature).get(); - } - catch (InterruptedException | ExecutionException e) { - throw new SQLServerException(SQLServerException.getErrString("R_VerifySignature"), e); - } + KeyVerifyResult valid = keyVaultClient.verify(masterKeyPath, JsonWebKeySignatureAlgorithm.RS256, dataToVerify, signature); - return valid; + return valid.value(); } /** @@ -537,21 +510,22 @@ private boolean AzureKeyVaultVerifySignature(byte[] dataToVerify, * when an error occurs */ private int getAKVKeySize(String masterKeyPath) throws SQLServerException { + KeyBundle retrievedKey = keyVaultClient.getKey(masterKeyPath); - KeyBundle retrievedKey = null; - try { - retrievedKey = keyVaultClient.getKeyAsync(masterKeyPath).get(); - } - catch (InterruptedException | ExecutionException e) { - throw new SQLServerException(SQLServerException.getErrString("R_GetAKVKeySize"), e); + if (null == retrievedKey) { + String[] keyTokens = masterKeyPath.split("/"); + + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_AKVKeyNotFound")); + Object[] msgArgs = {keyTokens[keyTokens.length - 1]}; + throw new SQLServerException(null, form.format(msgArgs), null, 0, false); } - if (!"RSA".equalsIgnoreCase(retrievedKey.getKey().getKty()) && !"RSA-HSM".equalsIgnoreCase(retrievedKey.getKey().getKty())) { + if (!"RSA".equalsIgnoreCase(retrievedKey.key().kty().toString()) && !"RSA-HSM".equalsIgnoreCase(retrievedKey.key().kty().toString())) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NonRSAKey")); - Object[] msgArgs = {retrievedKey.getKey().getKty()}; + Object[] msgArgs = {retrievedKey.key().kty().toString()}; throw new SQLServerException(null, form.format(msgArgs), null, 0, false); } - return retrievedKey.getKey().getN().length; + return retrievedKey.key().n().length; } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index a243203754..487c99c389 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -1357,7 +1357,7 @@ Connection connectInternal(Properties propsIn, if (sPropValue == null) sPropValue = SQLServerDriverStringProperty.SELECT_METHOD.getDefaultValue(); if ("cursor".equalsIgnoreCase(sPropValue) || "direct".equalsIgnoreCase(sPropValue)) { - activeConnectionProperties.setProperty(sPropKey, sPropValue.toLowerCase()); + activeConnectionProperties.setProperty(sPropKey, sPropValue.toLowerCase(Locale.ENGLISH)); } else { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidselectMethod")); @@ -1370,7 +1370,7 @@ Connection connectInternal(Properties propsIn, if (sPropValue == null) sPropValue = SQLServerDriverStringProperty.RESPONSE_BUFFERING.getDefaultValue(); if ("full".equalsIgnoreCase(sPropValue) || "adaptive".equalsIgnoreCase(sPropValue)) { - activeConnectionProperties.setProperty(sPropKey, sPropValue.toLowerCase()); + activeConnectionProperties.setProperty(sPropKey, sPropValue.toLowerCase(Locale.ENGLISH)); } else { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidresponseBuffering")); @@ -1398,7 +1398,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))).intValue(); + int n = new Integer(activeConnectionProperties.getProperty(sPropKey)); this.setStatementPoolingCacheSize(n); } catch (NumberFormatException e) { @@ -1514,7 +1514,7 @@ Connection connectInternal(Properties propsIn, throw new SQLServerException(SQLServerException.getErrString("R_AccessTokenWithUserPassword"), null); } - if ((!System.getProperty("os.name").toLowerCase().startsWith("windows")) + if ((!System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows")) && (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString()))) { throw new SQLServerException(SQLServerException.getErrString("R_AADIntegratedOnNonWindows"), null); } @@ -1535,7 +1535,7 @@ Connection connectInternal(Properties propsIn, try { String strPort = activeConnectionProperties.getProperty(sPropKey); if (null != strPort) { - nPort = (new Integer(strPort)).intValue(); + nPort = new Integer(strPort); if ((nPort < 0) || (nPort > 65535)) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPortNumber")); @@ -1615,7 +1615,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))).intValue(); + int n = new Integer(activeConnectionProperties.getProperty(sPropKey)); if (n >= defaultLockTimeOut) nLockTimeout = n; else { @@ -1636,7 +1636,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))).intValue(); + int n = new Integer(activeConnectionProperties.getProperty(sPropKey)); if (n >= defaultQueryTimeout) { queryTimeoutSeconds = n; } @@ -1658,7 +1658,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))).intValue(); + int n = new Integer(activeConnectionProperties.getProperty(sPropKey)); if (n >= defaultSocketTimeout) { socketTimeoutMilliseconds = n; } @@ -1678,7 +1678,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))).intValue(); + int n = new Integer(activeConnectionProperties.getProperty(sPropKey)); setServerPreparedStatementDiscardThreshold(n); } catch (NumberFormatException e) { @@ -2128,7 +2128,7 @@ ServerPortPlaceHolder primaryPermissionCheck(String primary, connectionlogger.fine(toString() + " SQL Server port returned by SQL Browser: " + instancePort); try { if (null != instancePort) { - primaryPortNumber = (new Integer(instancePort)).intValue(); + primaryPortNumber = new Integer(instancePort); if ((primaryPortNumber < 0) || (primaryPortNumber > 65535)) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPortNumber")); @@ -2838,7 +2838,7 @@ static String sqlStatementToSetCommit(boolean autoCommit) { public void setAutoCommit(boolean newAutoCommitMode) throws SQLServerException { if (loggerExternal.isLoggable(Level.FINER)) { - loggerExternal.entering(getClassNameLogging(), "setAutoCommit", Boolean.valueOf(newAutoCommitMode)); + loggerExternal.entering(getClassNameLogging(), "setAutoCommit", newAutoCommitMode); if (Util.IsActivityTraceOn()) loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); } @@ -2868,7 +2868,7 @@ public void setAutoCommit(boolean newAutoCommitMode) throws SQLServerException { checkClosed(); boolean res = !inXATransaction && databaseAutoCommitMode; if (loggerExternal.isLoggable(Level.FINER)) - loggerExternal.exiting(getClassNameLogging(), "getAutoCommit", Boolean.valueOf(res)); + loggerExternal.exiting(getClassNameLogging(), "getAutoCommit", res); return res; } @@ -2971,10 +2971,10 @@ public void close() throws SQLServerException { } // Invalidate statement caches. - if(null != preparedStatementHandleCache) + if (null != preparedStatementHandleCache) preparedStatementHandleCache.clear(); - if(null != parameterMetadataCache) + if (null != parameterMetadataCache) parameterMetadataCache.clear(); // Clean-up queue etc. related to batching of prepared statement discard actions (sp_unprepare). @@ -3008,7 +3008,7 @@ final void poolCloseEventNotify() throws SQLServerException { /* L0 */ public boolean isClosed() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "isClosed"); - loggerExternal.exiting(getClassNameLogging(), "isClosed", Boolean.valueOf(isSessionUnAvailable())); + loggerExternal.exiting(getClassNameLogging(), "isClosed", isSessionUnAvailable()); return isSessionUnAvailable(); } @@ -3024,7 +3024,7 @@ final void poolCloseEventNotify() throws SQLServerException { /* L0 */ public void setReadOnly(boolean readOnly) throws SQLServerException { if (loggerExternal.isLoggable(Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setReadOnly", Boolean.valueOf(readOnly)); + loggerExternal.entering(getClassNameLogging(), "setReadOnly", readOnly); checkClosed(); // do nothing per spec loggerExternal.exiting(getClassNameLogging(), "setReadOnly"); @@ -3034,7 +3034,7 @@ final void poolCloseEventNotify() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "isReadOnly"); checkClosed(); if (loggerExternal.isLoggable(Level.FINER)) - loggerExternal.exiting(getClassNameLogging(), "isReadOnly", Boolean.valueOf(false)); + loggerExternal.exiting(getClassNameLogging(), "isReadOnly", Boolean.FALSE); return false; } @@ -3060,7 +3060,7 @@ final void poolCloseEventNotify() throws SQLServerException { /* L0 */ public void setTransactionIsolation(int level) throws SQLServerException { if (loggerExternal.isLoggable(Level.FINER)) { - loggerExternal.entering(getClassNameLogging(), "setTransactionIsolation", new Integer(level)); + loggerExternal.entering(getClassNameLogging(), "setTransactionIsolation", level); if (Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); } @@ -3080,7 +3080,7 @@ final void poolCloseEventNotify() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getTransactionIsolation"); checkClosed(); if (loggerExternal.isLoggable(Level.FINER)) - loggerExternal.exiting(getClassNameLogging(), "getTransactionIsolation", new Integer(transactionIsolationLevel)); + loggerExternal.exiting(getClassNameLogging(), "getTransactionIsolation", transactionIsolationLevel); return transactionIsolationLevel; } @@ -3124,7 +3124,7 @@ public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLServerException { if (loggerExternal.isLoggable(Level.FINER)) loggerExternal.entering(getClassNameLogging(), "createStatement", - new Object[] {new Integer(resultSetType), new Integer(resultSetConcurrency)}); + new Object[] {resultSetType, resultSetConcurrency}); checkClosed(); Statement st = new SQLServerStatement(this, resultSetType, resultSetConcurrency, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); @@ -3137,7 +3137,7 @@ public PreparedStatement prepareStatement(String sql, int resultSetConcurrency) throws SQLServerException { if (loggerExternal.isLoggable(Level.FINER)) loggerExternal.entering(getClassNameLogging(), "prepareStatement", - new Object[] {sql, new Integer(resultSetType), new Integer(resultSetConcurrency)}); + new Object[] {sql, resultSetType, resultSetConcurrency}); checkClosed(); PreparedStatement st; @@ -3161,7 +3161,7 @@ private PreparedStatement prepareStatement(String sql, SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { if (loggerExternal.isLoggable(Level.FINER)) loggerExternal.entering(getClassNameLogging(), "prepareStatement", - new Object[] {sql, new Integer(resultSetType), new Integer(resultSetConcurrency), stmtColEncSetting}); + new Object[] {sql, resultSetType, resultSetConcurrency, stmtColEncSetting}); checkClosed(); PreparedStatement st; @@ -3182,7 +3182,7 @@ public CallableStatement prepareCall(String sql, int resultSetConcurrency) throws SQLServerException { if (loggerExternal.isLoggable(Level.FINER)) loggerExternal.entering(getClassNameLogging(), "prepareCall", - new Object[] {sql, new Integer(resultSetType), new Integer(resultSetConcurrency)}); + new Object[] {sql, resultSetType, resultSetConcurrency}); checkClosed(); CallableStatement st; @@ -4539,7 +4539,7 @@ public Statement createStatement(int nType, int nConcur, int resultSetHoldability) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "createStatement", - new Object[] {new Integer(nType), new Integer(nConcur), resultSetHoldability}); + new Object[] {nType, nConcur, resultSetHoldability}); Statement st = createStatement(nType, nConcur, resultSetHoldability, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); loggerExternal.exiting(getClassNameLogging(), "createStatement", st); return st; @@ -4550,7 +4550,7 @@ public Statement createStatement(int nType, int resultSetHoldability, SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "createStatement", - new Object[] {new Integer(nType), new Integer(nConcur), resultSetHoldability, stmtColEncSetting}); + new Object[] {nType, nConcur, resultSetHoldability, stmtColEncSetting}); checkClosed(); checkValidHoldability(resultSetHoldability); checkMatchesCurrentHoldability(resultSetHoldability); @@ -4564,7 +4564,7 @@ public Statement createStatement(int nType, int nConcur, int resultSetHoldability) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "prepareStatement", - new Object[] {new Integer(nType), new Integer(nConcur), resultSetHoldability}); + new Object[] {nType, nConcur, resultSetHoldability}); PreparedStatement st = prepareStatement(sql, nType, nConcur, resultSetHoldability, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); loggerExternal.exiting(getClassNameLogging(), "prepareStatement", st); @@ -4603,7 +4603,7 @@ public PreparedStatement prepareStatement(java.lang.String sql, int resultSetHoldability, SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "prepareStatement", - new Object[] {new Integer(nType), new Integer(nConcur), resultSetHoldability, stmtColEncSetting}); + new Object[] {nType, nConcur, resultSetHoldability, stmtColEncSetting}); checkClosed(); checkValidHoldability(resultSetHoldability); checkMatchesCurrentHoldability(resultSetHoldability); @@ -4626,7 +4626,7 @@ public PreparedStatement prepareStatement(java.lang.String sql, int nConcur, int resultSetHoldability) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "prepareStatement", - new Object[] {new Integer(nType), new Integer(nConcur), resultSetHoldability}); + new Object[] {nType, nConcur, resultSetHoldability}); CallableStatement st = prepareCall(sql, nType, nConcur, resultSetHoldability, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); loggerExternal.exiting(getClassNameLogging(), "prepareCall", st); return st; @@ -4638,7 +4638,7 @@ public CallableStatement prepareCall(String sql, int resultSetHoldability, SQLServerStatementColumnEncryptionSetting stmtColEncSetiing) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "prepareStatement", - new Object[] {new Integer(nType), new Integer(nConcur), resultSetHoldability, stmtColEncSetiing}); + new Object[] {nType, nConcur, resultSetHoldability, stmtColEncSetiing}); checkClosed(); checkValidHoldability(resultSetHoldability); checkMatchesCurrentHoldability(resultSetHoldability); @@ -4660,7 +4660,7 @@ public CallableStatement prepareCall(String sql, /* L3 */ public PreparedStatement prepareStatement(String sql, int flag) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "prepareStatement", new Object[] {sql, new Integer(flag)}); + loggerExternal.entering(getClassNameLogging(), "prepareStatement", new Object[] {sql, flag}); SQLServerPreparedStatement ps = (SQLServerPreparedStatement) prepareStatement(sql, flag, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); @@ -4699,7 +4699,7 @@ public CallableStatement prepareCall(String sql, public PreparedStatement prepareStatement(String sql, int flag, SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "prepareStatement", new Object[] {sql, new Integer(flag), stmtColEncSetting}); + loggerExternal.entering(getClassNameLogging(), "prepareStatement", new Object[] {sql, flag, stmtColEncSetting}); checkClosed(); SQLServerPreparedStatement ps = (SQLServerPreparedStatement) prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, stmtColEncSetting); @@ -5184,7 +5184,7 @@ public boolean isValid(int timeout) throws SQLException { public boolean isWrapperFor(Class iface) throws SQLException { loggerExternal.entering(getClassNameLogging(), "isWrapperFor", iface); boolean f = iface.isInstance(this); - loggerExternal.exiting(getClassNameLogging(), "isWrapperFor", Boolean.valueOf(f)); + loggerExternal.exiting(getClassNameLogging(), "isWrapperFor", f); return f; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java index b94bb4d6f8..25c269f83c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java @@ -686,16 +686,16 @@ public void setEnablePrepareOnFirstPreparedStatementCall(boolean enablePrepareOn } /** - * If this configuration returns false the first execution of a prepared statement will call sp_executesql and not prepare - * a statement, once the second execution happens it will call sp_prepexec and actually setup a prepared statement handle. Following - * executions will call sp_execute. This relieves the need for sp_unprepare on prepared statement close if the statement is only - * executed once. + * If this configuration returns false the first execution of a prepared statement will call sp_executesql and not prepare a statement, once the + * second execution happens it will call sp_prepexec and actually setup a prepared statement handle. Following executions will call sp_execute. + * This relieves the need for sp_unprepare on prepared statement close if the statement is only executed once. * * @return Returns the current setting per the description. */ public boolean getEnablePrepareOnFirstPreparedStatementCall() { boolean defaultValue = SQLServerDriverBooleanProperty.ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT.getDefaultValue(); - return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT.toString(), defaultValue); + return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT.toString(), + defaultValue); } /** @@ -728,7 +728,7 @@ public int getServerPreparedStatementDiscardThreshold() { * Specifies the size of the prepared statement cache for this conection. A value less than 1 means no cache. * * @param statementPoolingCacheSize - * Changes the setting per the description. + * Changes the setting per the description. */ public void setStatementPoolingCacheSize(int statementPoolingCacheSize) { setIntProperty(connectionProps, SQLServerDriverIntProperty.STATEMENT_POOLING_CACHE_SIZE.toString(), statementPoolingCacheSize); @@ -844,7 +844,7 @@ private void setIntProperty(Properties props, String propKey, int propValue) { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "set" + propKey, new Integer(propValue)); + loggerExternal.entering(getClassNameLogging(), "set" + propKey, propValue); props.setProperty(propKey, new Integer(propValue).toString()); loggerExternal.exiting(getClassNameLogging(), "set" + propKey); } @@ -870,7 +870,7 @@ private int getIntProperty(Properties props, } } if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.exiting(getClassNameLogging(), "get" + propKey, new Integer(value)); + loggerExternal.exiting(getClassNameLogging(), "get" + propKey, value); return value; } @@ -880,7 +880,7 @@ private void setBooleanProperty(Properties props, String propKey, boolean propValue) { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "set" + propKey, Boolean.valueOf(propValue)); + loggerExternal.entering(getClassNameLogging(), "set" + propKey, propValue); props.setProperty(propKey, (propValue) ? "true" : "false"); loggerExternal.exiting(getClassNameLogging(), "set" + propKey); } @@ -904,7 +904,7 @@ private boolean getBooleanProperty(Properties props, value = Boolean.valueOf(propValue); } loggerExternal.exiting(getClassNameLogging(), "get" + propKey, value); - return value.booleanValue(); + return value; } private void setObjectProperty(Properties props, @@ -1069,7 +1069,7 @@ else if (!"class".equals(propertyName)) { public boolean isWrapperFor(Class iface) throws SQLException { loggerExternal.entering(getClassNameLogging(), "isWrapperFor", iface); boolean f = iface.isInstance(this); - loggerExternal.exiting(getClassNameLogging(), "isWrapperFor", Boolean.valueOf(f)); + loggerExternal.exiting(getClassNameLogging(), "isWrapperFor", f); return f; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataTable.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataTable.java index 30085ed25b..f1436ceb56 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataTable.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataTable.java @@ -116,139 +116,13 @@ public synchronized void addRow(Object... values) throws SQLServerException { int currentColumn = 0; while (columnsIterator.hasNext()) { Object val = null; - boolean bValueNull; - int nValueLen; if ((null != values) && (currentColumn < values.length) && (null != values[currentColumn])) val = (null == values[currentColumn]) ? null : values[currentColumn]; currentColumn++; Map.Entry pair = columnsIterator.next(); - SQLServerDataColumn currentColumnMetadata = pair.getValue(); JDBCType jdbcType = JDBCType.of(pair.getValue().javaSqlType); - - boolean isColumnMetadataUpdated = false; - switch (jdbcType) { - case BIGINT: - rowValues[pair.getKey()] = (null == val) ? null : Long.parseLong(val.toString()); - break; - - case BIT: - rowValues[pair.getKey()] = (null == val) ? null : Boolean.parseBoolean(val.toString()); - break; - - case INTEGER: - rowValues[pair.getKey()] = (null == val) ? null : Integer.parseInt(val.toString()); - break; - - case SMALLINT: - case TINYINT: - rowValues[pair.getKey()] = (null == val) ? null : Short.parseShort(val.toString()); - break; - - case DECIMAL: - case NUMERIC: - BigDecimal bd = null; - if (null != val) { - bd = new BigDecimal(val.toString()); - // BigDecimal#precision returns number of digits in the unscaled value. - // Say, for value 0.01, it returns 1 but the precision should be 3 for SQLServer - int precision = Util.getValueLengthBaseOnJavaType(bd, JavaType.of(bd), null, null, jdbcType); - if (bd.scale() > currentColumnMetadata.scale) { - currentColumnMetadata.scale = bd.scale(); - isColumnMetadataUpdated = true; - } - if (precision > currentColumnMetadata.precision) { - currentColumnMetadata.precision = precision; - isColumnMetadataUpdated = true; - } - - // precision equal: the maximum number of digits in integer part + the maximum scale - int numberOfDigitsIntegerPart = precision - bd.scale(); - if (numberOfDigitsIntegerPart > currentColumnMetadata.numberOfDigitsIntegerPart) { - currentColumnMetadata.numberOfDigitsIntegerPart = numberOfDigitsIntegerPart; - isColumnMetadataUpdated = true; - } - - if (isColumnMetadataUpdated) { - currentColumnMetadata.precision = currentColumnMetadata.scale + currentColumnMetadata.numberOfDigitsIntegerPart; - columnMetadata.put(pair.getKey(), currentColumnMetadata); - } - } - rowValues[pair.getKey()] = bd; - break; - - case DOUBLE: - rowValues[pair.getKey()] = (null == val) ? null : Double.parseDouble(val.toString()); - break; - - case FLOAT: - case REAL: - rowValues[pair.getKey()] = (null == val) ? null : Float.parseFloat(val.toString()); - break; - - case TIMESTAMP_WITH_TIMEZONE: - case TIME_WITH_TIMEZONE: - DriverJDBCVersion.checkSupportsJDBC42(); - case DATE: - case TIME: - case TIMESTAMP: - case DATETIMEOFFSET: - // Sending temporal types as string. Error from database is thrown if parsing fails - // no need to send precision for temporal types, string literal will never exceed DataTypes.SHORT_VARTYPE_MAX_BYTES - - if (null == val) - rowValues[pair.getKey()] = null; - // java.sql.Date, java.sql.Time and java.sql.Timestamp are subclass of java.util.Date - else if (val instanceof java.util.Date) - rowValues[pair.getKey()] = val.toString(); - else if (val instanceof microsoft.sql.DateTimeOffset) - rowValues[pair.getKey()] = val.toString(); - else if (val instanceof OffsetDateTime) - rowValues[pair.getKey()] = val.toString(); - else if (val instanceof OffsetTime) - rowValues[pair.getKey()] = val.toString(); - else - rowValues[pair.getKey()] = (null == val) ? null : (String) val; - break; - - case BINARY: - case VARBINARY: - case LONGVARBINARY: - bValueNull = (null == val); - nValueLen = bValueNull ? 0 : ((byte[]) val).length; - - if (nValueLen > currentColumnMetadata.precision) { - currentColumnMetadata.precision = nValueLen; - columnMetadata.put(pair.getKey(), currentColumnMetadata); - } - rowValues[pair.getKey()] = (bValueNull) ? null : (byte[]) val; - - break; - - case CHAR: - if (val instanceof UUID && (val != null)) - val = val.toString(); - case VARCHAR: - case NCHAR: - case NVARCHAR: - case LONGVARCHAR: - case LONGNVARCHAR: - case SQLXML: - bValueNull = (null == val); - nValueLen = bValueNull ? 0 : (2 * ((String) val).length()); - - if (nValueLen > currentColumnMetadata.precision) { - currentColumnMetadata.precision = nValueLen; - columnMetadata.put(pair.getKey(), currentColumnMetadata); - } - rowValues[pair.getKey()] = (bValueNull) ? null : (String) val; - break; - - default: - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unsupportedDataTypeTVP")); - Object[] msgArgs = {jdbcType}; - throw new SQLServerException(null, form.format(msgArgs), null, 0, false); - } + internalAddrow(jdbcType, val, rowValues, pair); } rows.put(rowCount++, rowValues); } @@ -260,11 +134,160 @@ else if (val instanceof OffsetTime) } } - + + /** + * Adding rows one row of data to data table. + * @param jdbcType The jdbcType + * @param val The data value + * @param rowValues Row of data + * @param pair pair to be added to data table + * @throws SQLServerException + */ + private void internalAddrow(JDBCType jdbcType, + Object val, + Object[] rowValues, + Map.Entry pair) throws SQLServerException { + + SQLServerDataColumn currentColumnMetadata = pair.getValue(); + boolean isColumnMetadataUpdated = false; + boolean bValueNull; + int nValueLen; + switch (jdbcType) { + case BIGINT: + rowValues[pair.getKey()] = (null == val) ? null : Long.parseLong(val.toString()); + break; + + case BIT: + rowValues[pair.getKey()] = (null == val) ? null : Boolean.parseBoolean(val.toString()); + break; + + case INTEGER: + rowValues[pair.getKey()] = (null == val) ? null : Integer.parseInt(val.toString()); + break; + + case SMALLINT: + case TINYINT: + rowValues[pair.getKey()] = (null == val) ? null : Short.parseShort(val.toString()); + break; + + case DECIMAL: + case NUMERIC: + BigDecimal bd = null; + if (null != val) { + bd = new BigDecimal(val.toString()); + // BigDecimal#precision returns number of digits in the unscaled value. + // Say, for value 0.01, it returns 1 but the precision should be 3 for SQLServer + int precision = Util.getValueLengthBaseOnJavaType(bd, JavaType.of(bd), null, null, jdbcType); + if (bd.scale() > currentColumnMetadata.scale) { + currentColumnMetadata.scale = bd.scale(); + isColumnMetadataUpdated = true; + } + if (precision > currentColumnMetadata.precision) { + currentColumnMetadata.precision = precision; + isColumnMetadataUpdated = true; + } + + // precision equal: the maximum number of digits in integer part + the maximum scale + int numberOfDigitsIntegerPart = precision - bd.scale(); + if (numberOfDigitsIntegerPart > currentColumnMetadata.numberOfDigitsIntegerPart) { + currentColumnMetadata.numberOfDigitsIntegerPart = numberOfDigitsIntegerPart; + isColumnMetadataUpdated = true; + } + + if (isColumnMetadataUpdated) { + currentColumnMetadata.precision = currentColumnMetadata.scale + currentColumnMetadata.numberOfDigitsIntegerPart; + columnMetadata.put(pair.getKey(), currentColumnMetadata); + } + } + rowValues[pair.getKey()] = bd; + break; + + case DOUBLE: + rowValues[pair.getKey()] = (null == val) ? null : Double.parseDouble(val.toString()); + break; + + case FLOAT: + case REAL: + rowValues[pair.getKey()] = (null == val) ? null : Float.parseFloat(val.toString()); + break; + + case TIMESTAMP_WITH_TIMEZONE: + case TIME_WITH_TIMEZONE: + DriverJDBCVersion.checkSupportsJDBC42(); + case DATE: + case TIME: + case TIMESTAMP: + case DATETIMEOFFSET: + // Sending temporal types as string. Error from database is thrown if parsing fails + // no need to send precision for temporal types, string literal will never exceed DataTypes.SHORT_VARTYPE_MAX_BYTES + + if (null == val) + rowValues[pair.getKey()] = null; + // java.sql.Date, java.sql.Time and java.sql.Timestamp are subclass of java.util.Date + else if (val instanceof java.util.Date) + rowValues[pair.getKey()] = val.toString(); + else if (val instanceof microsoft.sql.DateTimeOffset) + rowValues[pair.getKey()] = val.toString(); + else if (val instanceof OffsetDateTime) + rowValues[pair.getKey()] = val.toString(); + else if (val instanceof OffsetTime) + rowValues[pair.getKey()] = val.toString(); + else + rowValues[pair.getKey()] = (null == val) ? null : (String) val; + break; + + case BINARY: + case VARBINARY: + case LONGVARBINARY: + bValueNull = (null == val); + nValueLen = bValueNull ? 0 : ((byte[]) val).length; + + if (nValueLen > currentColumnMetadata.precision) { + currentColumnMetadata.precision = nValueLen; + columnMetadata.put(pair.getKey(), currentColumnMetadata); + } + rowValues[pair.getKey()] = (bValueNull) ? null : (byte[]) val; + + break; + + case CHAR: + if (val instanceof UUID && (val != null)) + val = val.toString(); + case VARCHAR: + case NCHAR: + case NVARCHAR: + case LONGVARCHAR: + case LONGNVARCHAR: + case SQLXML: + bValueNull = (null == val); + nValueLen = bValueNull ? 0 : (2 * ((String) val).length()); + + if (nValueLen > currentColumnMetadata.precision) { + currentColumnMetadata.precision = nValueLen; + columnMetadata.put(pair.getKey(), currentColumnMetadata); + } + rowValues[pair.getKey()] = (bValueNull) ? null : (String) val; + break; + case SQL_VARIANT: + JDBCType internalJDBCType; + if (null == val) { // TODO:Check this later + throw new SQLServerException(SQLServerException.getErrString("R_invalidValueForTVPWithSQLVariant"), null); + } + JavaType javaType = JavaType.of(val); + internalJDBCType = javaType.getJDBCType(SSType.UNKNOWN, jdbcType); + internalAddrow(internalJDBCType, val, rowValues, pair); + break; + default: + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unsupportedDataTypeTVP")); + Object[] msgArgs = {jdbcType}; + throw new SQLServerException(null, form.format(msgArgs), null, 0, false); + } + } + public synchronized Map getColumnMetadata() { return columnMetadata; } - + public String getTvpName() { return tvpName; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index fd732343c3..25e7a7c146 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -1917,7 +1917,7 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { } // if the value is outside of the valid values throw error. MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument")); - Object[] msgArgs = {new Integer(type)}; + Object[] msgArgs = {type}; throw new SQLServerException(null, form.format(msgArgs), null, 0, true); } @@ -1933,7 +1933,7 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { } // if the value is outside of the valid values throw error. MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument")); - Object[] msgArgs = {new Integer(type)}; + Object[] msgArgs = {type}; throw new SQLServerException(null, form.format(msgArgs), null, 0, true); } @@ -1988,7 +1988,7 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { if (p > 0) s = s.substring(0, p); try { - return new Integer(s).intValue(); + return new Integer(s); } catch (NumberFormatException e) { return 0; @@ -2003,7 +2003,7 @@ else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) { if (p > 0 && q > 0) s = s.substring(p + 1, q); try { - return new Integer(s).intValue(); + return new Integer(s); } catch (NumberFormatException e) { return 0; @@ -2038,7 +2038,7 @@ public RowIdLifetime getRowIdLifetime() throws SQLException { // if the value is outside of the valid values throw error. MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument")); - Object[] msgArgs = {new Integer(holdability)}; + Object[] msgArgs = {holdability}; throw new SQLServerException(null, form.format(msgArgs), null, 0, true); } @@ -2202,12 +2202,12 @@ final Object apply(Object value, switch (asJDBCType) { case INTEGER: - return new Integer(oneValueToAnother(((Integer) value).intValue())); + return oneValueToAnother((Integer) value); case SMALLINT: // small and tinyint returned as short case TINYINT: - return new Short((short) oneValueToAnother(((Short) value).intValue())); + return (short) oneValueToAnother(((Short) value).intValue()); case BIGINT: - return new Long(oneValueToAnother(((Long) value).intValue())); + return (long) oneValueToAnother(((Long) value).intValue()); case CHAR: case VARCHAR: case LONGVARCHAR: diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java index d3fe42c615..bfe9612202 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java @@ -268,12 +268,12 @@ public String toString() { } enum SQLServerDriverIntProperty { - PACKET_SIZE ("packetSize", TDS.DEFAULT_PACKET_SIZE), - LOCK_TIMEOUT ("lockTimeout", -1), - LOGIN_TIMEOUT ("loginTimeout", 15), - QUERY_TIMEOUT ("queryTimeout", -1), - PORT_NUMBER ("portNumber", 1433), - SOCKET_TIMEOUT ("socketTimeout", 0), + PACKET_SIZE ("packetSize", TDS.DEFAULT_PACKET_SIZE), + LOCK_TIMEOUT ("lockTimeout", -1), + LOGIN_TIMEOUT ("loginTimeout", 15), + QUERY_TIMEOUT ("queryTimeout", -1), + PORT_NUMBER ("portNumber", 1433), + SOCKET_TIMEOUT ("socketTimeout", 0), SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD("serverPreparedStatementDiscardThreshold", SQLServerConnection.DEFAULT_SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD), STATEMENT_POOLING_CACHE_SIZE ("statementPoolingCacheSize", SQLServerConnection.DEFAULT_STATEMENT_POOLING_CACHE_SIZE), ; @@ -298,19 +298,19 @@ public String toString() { enum SQLServerDriverBooleanProperty { - DISABLE_STATEMENT_POOLING ("disableStatementPooling", false), - ENCRYPT ("encrypt", false), - INTEGRATED_SECURITY ("integratedSecurity", false), - LAST_UPDATE_COUNT ("lastUpdateCount", true), - MULTI_SUBNET_FAILOVER ("multiSubnetFailover", false), - SERVER_NAME_AS_ACE ("serverNameAsACE", false), - SEND_STRING_PARAMETERS_AS_UNICODE ("sendStringParametersAsUnicode", true), - SEND_TIME_AS_DATETIME ("sendTimeAsDatetime", true), - TRANSPARENT_NETWORK_IP_RESOLUTION ("TransparentNetworkIPResolution", true), - TRUST_SERVER_CERTIFICATE ("trustServerCertificate", false), - XOPEN_STATES ("xopenStates", false), - FIPS ("fips", false), - ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT("enablePrepareOnFirstPreparedStatementCall", SQLServerConnection.DEFAULT_ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT_CALL); + DISABLE_STATEMENT_POOLING ("disableStatementPooling", false), + ENCRYPT ("encrypt", false), + INTEGRATED_SECURITY ("integratedSecurity", false), + LAST_UPDATE_COUNT ("lastUpdateCount", true), + MULTI_SUBNET_FAILOVER ("multiSubnetFailover", false), + SERVER_NAME_AS_ACE ("serverNameAsACE", false), + SEND_STRING_PARAMETERS_AS_UNICODE ("sendStringParametersAsUnicode", true), + SEND_TIME_AS_DATETIME ("sendTimeAsDatetime", true), + TRANSPARENT_NETWORK_IP_RESOLUTION ("TransparentNetworkIPResolution", true), + TRUST_SERVER_CERTIFICATE ("trustServerCertificate", false), + XOPEN_STATES ("xopenStates", false), + FIPS ("fips", false), + ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT("enablePrepareOnFirstPreparedStatementCall", SQLServerConnection.DEFAULT_ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT_CALL); private final String name; private final boolean defaultValue; @@ -356,7 +356,7 @@ public final class SQLServerDriver implements java.sql.Driver { new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.LOCK_TIMEOUT.toString(), Integer.toString(SQLServerDriverIntProperty.LOCK_TIMEOUT.getDefaultValue()), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.LOGIN_TIMEOUT.toString(), Integer.toString(SQLServerDriverIntProperty.LOGIN_TIMEOUT.getDefaultValue()), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.MULTI_SUBNET_FAILOVER.toString(), Boolean.toString(SQLServerDriverBooleanProperty.MULTI_SUBNET_FAILOVER.getDefaultValue()), false, TRUE_FALSE), - new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.PACKET_SIZE.toString(), Integer.toString(SQLServerDriverIntProperty.PACKET_SIZE.getDefaultValue()), false, null), + new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.PACKET_SIZE.toString(), Integer.toString(SQLServerDriverIntProperty.PACKET_SIZE.getDefaultValue()), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.PASSWORD.toString(), SQLServerDriverStringProperty.PASSWORD.getDefaultValue(), true, null), new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.PORT_NUMBER.toString(), Integer.toString(SQLServerDriverIntProperty.PORT_NUMBER.getDefaultValue()), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.QUERY_TIMEOUT.toString(), Integer.toString(SQLServerDriverIntProperty.QUERY_TIMEOUT.getDefaultValue()), false, null), @@ -382,7 +382,7 @@ public final class SQLServerDriver implements java.sql.Driver { new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.FIPS.toString(), Boolean.toString(SQLServerDriverBooleanProperty.FIPS.getDefaultValue()), false, TRUE_FALSE), new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT.toString(), Boolean.toString(SQLServerDriverBooleanProperty.ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT.getDefaultValue()), false,TRUE_FALSE), new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD.toString(), Integer.toString(SQLServerDriverIntProperty.SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD.getDefaultValue()), false, null), - new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.STATEMENT_POOLING_CACHE_SIZE.toString(), Integer.toString(SQLServerDriverIntProperty.STATEMENT_POOLING_CACHE_SIZE.getDefaultValue()), false, null), + new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.STATEMENT_POOLING_CACHE_SIZE.toString(), Integer.toString(SQLServerDriverIntProperty.STATEMENT_POOLING_CACHE_SIZE.getDefaultValue()), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.JAAS_CONFIG_NAME.toString(), SQLServerDriverStringProperty.JAAS_CONFIG_NAME.getDefaultValue(), false, null), }; @@ -639,13 +639,13 @@ static final DriverPropertyInfo[] getPropertyInfoFromProperties(Properties props public int getMajorVersion() { loggerExternal.entering(getClassNameLogging(), "getMajorVersion"); - loggerExternal.exiting(getClassNameLogging(), "getMajorVersion", new Integer(SQLJdbcVersion.major)); + loggerExternal.exiting(getClassNameLogging(), "getMajorVersion", SQLJdbcVersion.major); return SQLJdbcVersion.major; } public int getMinorVersion() { loggerExternal.entering(getClassNameLogging(), "getMinorVersion"); - loggerExternal.exiting(getClassNameLogging(), "getMinorVersion", new Integer(SQLJdbcVersion.minor)); + loggerExternal.exiting(getClassNameLogging(), "getMinorVersion", SQLJdbcVersion.minor); return SQLJdbcVersion.minor; } @@ -655,7 +655,7 @@ public Logger getParentLogger() throws SQLFeatureNotSupportedException { /* L0 */ public boolean jdbcCompliant() { loggerExternal.entering(getClassNameLogging(), "jdbcCompliant"); - loggerExternal.exiting(getClassNameLogging(), "jdbcCompliant", Boolean.valueOf(true)); + loggerExternal.exiting(getClassNameLogging(), "jdbcCompliant", Boolean.TRUE); return true; } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java index 7d758420d2..2f4dfd06ca 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java @@ -125,10 +125,11 @@ static String getErrString(String errCode) { * Make a new SQLException * * @param errText - * the excception message - * @param errState - * the excpeption state + * the exception message + * @param sqlState + * the statement * @param driverError + * the driver error object * @param cause * The exception that caused this exception */ @@ -171,6 +172,7 @@ public SQLServerException(String errText, * Make a new SQLException * * @param obj + * the object * @param errText * the exception message * @param errState diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerKeyVaultAuthenticationCallback.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerKeyVaultAuthenticationCallback.java deleted file mode 100644 index c940dd8ebc..0000000000 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerKeyVaultAuthenticationCallback.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.jdbc; - -public interface SQLServerKeyVaultAuthenticationCallback { - - /** - * The authentication callback delegate which is to be implemented by the client code - * - * @param authority - * - Identifier of the authority, a URL. - * @param resource - * - Identifier of the target resource that is the recipient of the requested token, a URL. - * @param scope - * - The scope of the authentication request. - * @return access token - */ - public String getAccessToken(String authority, - String resource, - String scope); -} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerMetaData.java index c2ee1460d6..00614ff8bb 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerMetaData.java @@ -26,7 +26,8 @@ public class SQLServerMetaData { boolean isUniqueKey = false; SQLServerSortOrder sortOrder = SQLServerSortOrder.Unspecified; int sortOrdinal; - + private SQLCollation collation; + static final int defaultSortOrdinal = -1; /** @@ -186,6 +187,10 @@ public SQLServerSortOrder getSortOrder() { public int getSortOrdinal() { return sortOrdinal; } + + SQLCollation getCollation() { + return this.collation; + } void validateSortOrder() throws SQLServerException { // should specify both sort order and ordinal, or neither diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java index 5d8fc51104..04df112ea8 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java @@ -15,6 +15,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; @@ -51,6 +52,9 @@ public final class SQLServerParameterMetaData implements ParameterMetaData { static private final AtomicInteger baseID = new AtomicInteger(0); // Unique id generator for each instance (used for logging). final private String traceID = " SQLServerParameterMetaData:" + nextInstanceID(); boolean isTVP = false; + + private String stringToParse = null; + private int indexToBeginParse = -1; // Returns unique id for each instance. private static int nextInstanceID() { @@ -73,10 +77,11 @@ final public String toString() { * the list of columns * @param columnStartToken * the token that prfixes the column set + * @throws SQLServerException */ /* L2 */ private String parseColumns(String columnSet, - String columnStartToken) { - StringTokenizer st = new StringTokenizer(columnSet, " =?<>!", true); + String columnStartToken) throws SQLServerException { + StringTokenizer st = new StringTokenizer(columnSet, " =?<>!\r\n\t\f", true); final int START = 0; final int PARAMNAME = 1; final int PARAMVALUE = 2; @@ -85,9 +90,12 @@ final public String toString() { String sLastField = null; StringBuilder sb = new StringBuilder(); + int sTokenIndex = 0; while (st.hasMoreTokens()) { String sToken = st.nextToken(); + sTokenIndex = sTokenIndex + sToken.length(); + if (sToken.equalsIgnoreCase(columnStartToken)) { nState = PARAMNAME; continue; @@ -120,6 +128,7 @@ final public String toString() { } } + indexToBeginParse = sTokenIndex; return sb.toString(); } @@ -130,16 +139,20 @@ final public String toString() { * the sql syntax * @param columnMarker * the token that denotes the start of the column set + * @throws SQLServerException */ /* L2 */ private String parseInsertColumns(String sql, - String columnMarker) { + String columnMarker) throws SQLServerException { StringTokenizer st = new StringTokenizer(sql, " (),", true); int nState = 0; String sLastField = null; StringBuilder sb = new StringBuilder(); + int sTokenIndex = 0; while (st.hasMoreTokens()) { String sToken = st.nextToken(); + sTokenIndex = sTokenIndex + sToken.length(); + if (sToken.equalsIgnoreCase(columnMarker)) { nState = 1; continue; @@ -163,12 +176,18 @@ final public String toString() { } if (nState == 1) { if (sToken.trim().length() > 0) { - if (sToken.charAt(0) != ',') + if (sToken.charAt(0) != ',') { sLastField = escapeParse(st, sToken); + + // in case the parameter has braces in its name, e.g. [c2_nvarchar(max)], the original sToken variable just + // contains [c2_nvarchar, sLastField actually has the whole name [c2_nvarchar(max)] + sTokenIndex = sTokenIndex + (sLastField.length() - sToken.length()); + } } } } + indexToBeginParse = sTokenIndex; return sb.toString(); } @@ -233,7 +252,7 @@ else if (SSType.Category.CHARACTER == ssType.category || SSType.Category.BINARY } catch (NumberFormatException e) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_metaDataErrorForParameter")); - Object[] msgArgs = {new Integer(paramOrdinal)}; + Object[] msgArgs = {paramOrdinal}; SQLServerException.makeFromDriverError(con, stmtParent, form.format(msgArgs) + " " + e.toString(), null, false); } } @@ -322,23 +341,29 @@ private void parseQueryMetaFor2008(ResultSet rsQueryMeta) throws SQLServerExcept * @param st * string tokenizer * @param firstToken + * @throws SQLServerException * @returns the full token */ private String escapeParse(StringTokenizer st, - String firstToken) { - String nameFragment; - String fullName; - nameFragment = firstToken; + String firstToken) throws SQLServerException { + + if (null == firstToken) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); + Object[] msgArgs1 = {"firstToken"}; + throw new SQLServerException(form.format(msgArgs1), null); + } + // skip spaces - while (" ".equals(nameFragment) && st.hasMoreTokens()) { - nameFragment = st.nextToken(); + while ((0 == firstToken.trim().length()) && st.hasMoreTokens()) { + firstToken = st.nextToken(); } - fullName = nameFragment; - if (nameFragment.charAt(0) == '[' && nameFragment.charAt(nameFragment.length() - 1) != ']') { + + String fullName = firstToken; + if (firstToken.charAt(0) == '[' && firstToken.charAt(firstToken.length() - 1) != ']') { while (st.hasMoreTokens()) { - nameFragment = st.nextToken(); - fullName = fullName.concat(nameFragment); - if (nameFragment.charAt(nameFragment.length() - 1) == ']') { + firstToken = st.nextToken(); + fullName = fullName.concat(firstToken); + if (firstToken.charAt(firstToken.length() - 1) == ']') { break; } @@ -366,10 +391,11 @@ private class MetaInfo { * String * @param sTableMarker * the location of the table in the syntax + * @throws SQLServerException */ private MetaInfo parseStatement(String sql, - String sTableMarker) { - StringTokenizer st = new StringTokenizer(sql, " ,\r\n", true); + String sTableMarker) throws SQLServerException { + StringTokenizer st = new StringTokenizer(sql, " ,\r\n\t\f(", true); /* Find the table */ @@ -391,12 +417,24 @@ private MetaInfo parseStatement(String sql, } if (null != metaTable) { - if (sTableMarker.equalsIgnoreCase("UPDATE")) + if (sTableMarker.equalsIgnoreCase("UPDATE")) { metaFields = parseColumns(sql, "SET"); // Get the set fields - else if (sTableMarker.equalsIgnoreCase("INTO")) // insert + stringToParse = ""; + } + else if (sTableMarker.equalsIgnoreCase("INTO")) { // insert metaFields = parseInsertColumns(sql, "("); // Get the value fields - else + stringToParse = sql.substring(indexToBeginParse); // the index of ')' + + // skip VALUES() clause + if (stringToParse.trim().toLowerCase().startsWith("values")) { + parseInsertColumns(stringToParse, "("); + stringToParse = stringToParse.substring(indexToBeginParse); // the index of ')' + } + } + else { metaFields = parseColumns(sql, "WHERE"); // Get the where fields + stringToParse = ""; + } return new MetaInfo(metaTable, metaFields); } @@ -412,7 +450,7 @@ else if (sTableMarker.equalsIgnoreCase("INTO")) // insert * @throws SQLServerException */ private MetaInfo parseStatement(String sql) throws SQLServerException { - StringTokenizer st = new StringTokenizer(sql, " "); + StringTokenizer st = new StringTokenizer(sql, " \r\n\t\f"); if (st.hasMoreTokens()) { String sToken = st.nextToken().trim(); @@ -577,20 +615,54 @@ private void checkClosed() throws SQLServerException { } else { // old implementation for SQL server 2008 - MetaInfo metaInfo = parseStatement(sProcString); - if (null == metaInfo) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_cantIdentifyTableMetadata")); - Object[] msgArgs = {sProcString}; - SQLServerException.makeFromDriverError(con, stmtParent, form.format(msgArgs), null, false); - } + stringToParse = sProcString; + ArrayList metaInfoList = new ArrayList(); + + while (stringToParse.length() > 0) { + MetaInfo metaInfo = parseStatement(stringToParse); + if (null == metaInfo) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_cantIdentifyTableMetadata")); + Object[] msgArgs = {stringToParse}; + SQLServerException.makeFromDriverError(con, stmtParent, form.format(msgArgs), null, false); + } - if (metaInfo.fields.length() <= 0) + metaInfoList.add(metaInfo); + } + if (metaInfoList.size() <= 0 || metaInfoList.get(0).fields.length() <= 0) { return; + } + + StringBuilder sbColumns = new StringBuilder(); + + for (MetaInfo mi : metaInfoList) { + sbColumns = sbColumns.append(mi.fields + ","); + } + sbColumns.deleteCharAt(sbColumns.length() - 1); + + String columns = sbColumns.toString(); + + StringBuilder sbTablesAndJoins = new StringBuilder(); + for (int i = 0; i < metaInfoList.size(); i++) { + if (i == 0) { + sbTablesAndJoins = sbTablesAndJoins.append(metaInfoList.get(i).table); + } + else { + if (metaInfoList.get(i).table.equals(metaInfoList.get(i - 1).table) + && metaInfoList.get(i).fields.equals(metaInfoList.get(i - 1).fields)) { + continue; + } + sbTablesAndJoins = sbTablesAndJoins + .append(" LEFT JOIN " + metaInfoList.get(i).table + " ON " + metaInfoList.get(i - 1).table + "." + + metaInfoList.get(i - 1).fields + "=" + metaInfoList.get(i).table + "." + metaInfoList.get(i).fields); + } + } + + String tablesAndJoins = sbTablesAndJoins.toString(); Statement stmt = con.createStatement(); - String sCom = "sp_executesql N'SET FMTONLY ON SELECT " + metaInfo.fields + " FROM " + metaInfo.table + " WHERE 1 = 2'"; - ResultSet rs = stmt.executeQuery(sCom); + String sCom = "sp_executesql N'SET FMTONLY ON SELECT " + columns + " FROM " + tablesAndJoins + " '"; + ResultSet rs = stmt.executeQuery(sCom); parseQueryMetaFor2008(rs); stmt.close(); rs.close(); @@ -637,12 +709,12 @@ public T unwrap(Class iface) throws SQLException { } catch (SQLException e) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_metaDataErrorForParameter")); - Object[] msgArgs = {new Integer(param)}; + Object[] msgArgs = {param}; SQLServerException.makeFromDriverError(con, stmtParent, form.format(msgArgs) + " " + e.toString(), null, false); } if (!bFound) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidParameterNumber")); - Object[] msgArgs = {new Integer(param)}; + Object[] msgArgs = {param}; SQLServerException.makeFromDriverError(con, stmtParent, form.format(msgArgs), null, false); } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 4bfcb58e2f..2b9350e490 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -235,7 +235,7 @@ final boolean doExecute() throws SQLServerException { tdsWriter.writeShort(executedSqlDirectly ? TDS.PROCID_SP_UNPREPARE : TDS.PROCID_SP_CURSORUNPREPARE); tdsWriter.writeByte((byte) 0); // RPC procedure option 1 tdsWriter.writeByte((byte) 0); // RPC procedure option 2 - tdsWriter.writeRPCInt(null, new Integer(handleToClose), false); + tdsWriter.writeRPCInt(null, handleToClose, false); TDSParser.parse(startResponse(), getLogContext()); return true; } @@ -355,7 +355,7 @@ private String buildParamTypeDefinitions(Parameter[] params, String typeDefinition = params[i].getTypeDefinition(connection, resultsReader()); if (null == typeDefinition) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_valueNotSetForParameter")); - Object[] msgArgs = {new Integer(i + 1)}; + Object[] msgArgs = {i + 1}; SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), null, false); } @@ -411,7 +411,7 @@ public int executeUpdate() throws SQLServerException { if (updateCount < Integer.MIN_VALUE || updateCount > Integer.MAX_VALUE) SQLServerException.makeFromDriverError(connection, this, SQLServerException.getErrString("R_updateCountOutofRange"), null, true); - loggerExternal.exiting(getClassNameLogging(), "executeUpdate", new Long(updateCount)); + loggerExternal.exiting(getClassNameLogging(), "executeUpdate", updateCount); return (int) updateCount; } @@ -425,7 +425,7 @@ public long executeLargeUpdate() throws SQLServerException { } checkClosed(); executeStatement(new PrepStmtExecCmd(this, EXECUTE_UPDATE)); - loggerExternal.exiting(getClassNameLogging(), "executeLargeUpdate", new Long(updateCount)); + loggerExternal.exiting(getClassNameLogging(), "executeLargeUpdate", updateCount); return updateCount; } @@ -443,7 +443,7 @@ public boolean execute() throws SQLServerException { } checkClosed(); executeStatement(new PrepStmtExecCmd(this, EXECUTE)); - loggerExternal.exiting(getClassNameLogging(), "execute", Boolean.valueOf(null != resultSet)); + loggerExternal.exiting(getClassNameLogging(), "execute", null != resultSet); return null != resultSet; } @@ -577,7 +577,7 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException { setPreparedStatementHandle(param.getInt(tdsReader)); // Cache the reference to the newly created handle, NOT for cursorable handles. - if (null == cachedPreparedStatementHandle && !isCursorable(executeMethod)) { + if (null == cachedPreparedStatementHandle && !isCursorable(executeMethod)) { cachedPreparedStatementHandle = connection.registerCachedPreparedStatementHandle(new Sha1HashKey(preparedSQL, preparedTypeDefinitions), prepStmtHandle, executedSqlDirectly); } @@ -631,11 +631,11 @@ private void buildServerCursorPrepExecParams(TDSWriter tdsWriter) throws SQLServ // // IN (reprepare): Old handle to unprepare before repreparing // OUT: The newly prepared handle - tdsWriter.writeRPCInt(null, new Integer(getPreparedStatementHandle()), true); + tdsWriter.writeRPCInt(null, getPreparedStatementHandle(), true); resetPrepStmtHandle(); // OUT - tdsWriter.writeRPCInt(null, new Integer(0), true); // cursor ID (OUTPUT) + tdsWriter.writeRPCInt(null, 0, true); // cursor ID (OUTPUT) // IN tdsWriter.writeRPCStringUnicode((preparedTypeDefinitions.length() > 0) ? preparedTypeDefinitions : null); @@ -647,13 +647,13 @@ private void buildServerCursorPrepExecParams(TDSWriter tdsWriter) throws SQLServ // Note: we must strip out SCROLLOPT_PARAMETERIZED_STMT if we don't // actually have any parameters. tdsWriter.writeRPCInt(null, - new Integer(getResultSetScrollOpt() & ~((0 == preparedTypeDefinitions.length()) ? TDS.SCROLLOPT_PARAMETERIZED_STMT : 0)), false); + getResultSetScrollOpt() & ~((0 == preparedTypeDefinitions.length()) ? TDS.SCROLLOPT_PARAMETERIZED_STMT : 0), false); // IN - tdsWriter.writeRPCInt(null, new Integer(getResultSetCCOpt()), false); + tdsWriter.writeRPCInt(null, getResultSetCCOpt(), false); // OUT - tdsWriter.writeRPCInt(null, new Integer(0), true); + tdsWriter.writeRPCInt(null, 0, true); } private void buildPrepExecParams(TDSWriter tdsWriter) throws SQLServerException { @@ -673,7 +673,7 @@ private void buildPrepExecParams(TDSWriter tdsWriter) throws SQLServerException // // IN (reprepare): Old handle to unprepare before repreparing // OUT: The newly prepared handle - tdsWriter.writeRPCInt(null, new Integer(getPreparedStatementHandle()), true); + tdsWriter.writeRPCInt(null, getPreparedStatementHandle(), true); resetPrepStmtHandle(); // IN @@ -704,7 +704,8 @@ private void buildExecSQLParams(TDSWriter tdsWriter) throws SQLServerException { tdsWriter.writeRPCStringUnicode(preparedSQL); // IN - tdsWriter.writeRPCStringUnicode((preparedTypeDefinitions.length() > 0) ? preparedTypeDefinitions : null); + if (preparedTypeDefinitions.length() > 0) + tdsWriter.writeRPCStringUnicode(preparedTypeDefinitions); } private void buildServerCursorExecParams(TDSWriter tdsWriter) throws SQLServerException { @@ -723,19 +724,19 @@ private void buildServerCursorExecParams(TDSWriter tdsWriter) throws SQLServerEx // IN assert hasPreparedStatementHandle(); - tdsWriter.writeRPCInt(null, new Integer(getPreparedStatementHandle()), false); + tdsWriter.writeRPCInt(null, getPreparedStatementHandle(), false); // OUT - tdsWriter.writeRPCInt(null, new Integer(0), true); + tdsWriter.writeRPCInt(null, 0, true); // IN - tdsWriter.writeRPCInt(null, new Integer(getResultSetScrollOpt() & ~TDS.SCROLLOPT_PARAMETERIZED_STMT), false); + tdsWriter.writeRPCInt(null, getResultSetScrollOpt() & ~TDS.SCROLLOPT_PARAMETERIZED_STMT, false); // IN - tdsWriter.writeRPCInt(null, new Integer(getResultSetCCOpt()), false); + tdsWriter.writeRPCInt(null, getResultSetCCOpt(), false); // OUT - tdsWriter.writeRPCInt(null, new Integer(0), true); + tdsWriter.writeRPCInt(null, 0, true); } private void buildExecParams(TDSWriter tdsWriter) throws SQLServerException { @@ -754,7 +755,7 @@ private void buildExecParams(TDSWriter tdsWriter) throws SQLServerException { // IN assert hasPreparedStatementHandle(); - tdsWriter.writeRPCInt(null, new Integer(getPreparedStatementHandle()), false); + tdsWriter.writeRPCInt(null, getPreparedStatementHandle(), false); } private void getParameterEncryptionMetadata(Parameter[] params) throws SQLServerException { @@ -922,15 +923,17 @@ private boolean reuseCachedHandle(boolean hasNewTypeDefinitions, boolean discard if(hasNewTypeDefinitions) { if (null != cachedPreparedStatementHandle && hasPreparedStatementHandle() && prepStmtHandle == cachedPreparedStatementHandle.getHandle()) { cachedPreparedStatementHandle.removeReference(); + cachedPreparedStatementHandle.setIsExplicitlyDiscarded(); } - cachedPreparedStatementHandle = null; + cachedPreparedStatementHandle = null; } - // Check for new cache reference. + // Check for new cache reference. if (null == cachedPreparedStatementHandle) { PreparedStatementHandle cachedHandle = connection.getCachedPreparedStatementHandle(new Sha1HashKey(preparedSQL, preparedTypeDefinitions)); - // If handle was found then re-use, only if AE is not on, or if it is on, make sure encryptionMetadataIsRetrieved is retrieved. + // If handle was found then re-use, only if AE is not on and is not a batch query with new type definitions (We shouldn't reuse handle + // if it is batch query and has new type definition, or if it is on, make sure encryptionMetadataIsRetrieved is retrieved. if (null != cachedHandle) { if (!connection.isColumnEncryptionSettingEnabled() || (connection.isColumnEncryptionSettingEnabled() && encryptionMetadataIsRetrieved)) { @@ -943,7 +946,7 @@ private boolean reuseCachedHandle(boolean hasNewTypeDefinitions, boolean discard } } return false; - } + } private boolean doPrepExec(TDSWriter tdsWriter, Parameter[] params, @@ -1049,7 +1052,7 @@ else if (resultSet != null) { /* L0 */ final Parameter setterGetParam(int index) throws SQLServerException { if (index < 1 || index > inOutParam.length) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_indexOutOfRange")); - Object[] msgArgs = {new Integer(index)}; + Object[] msgArgs = {index}; SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), "07009", false); } @@ -1144,7 +1147,7 @@ private Parameter getParam(int index) throws SQLServerException { index--; if (index < 0 || index >= inOutParam.length) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_indexOutOfRange")); - Object[] msgArgs = {new Integer(index + 1)}; + Object[] msgArgs = {index + 1}; SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), "07009", false); } return inOutParam[index]; @@ -1340,7 +1343,7 @@ public final void setBoolean(int n, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBoolean", new Object[] {n, x}); checkClosed(); - setValue(n, JDBCType.BIT, Boolean.valueOf(x), JavaType.BOOLEAN, false); + setValue(n, JDBCType.BIT, x, JavaType.BOOLEAN, false); loggerExternal.exiting(getClassNameLogging(), "setBoolean"); } @@ -1365,7 +1368,7 @@ public final void setBoolean(int n, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBoolean", new Object[] {n, x, forceEncrypt}); checkClosed(); - setValue(n, JDBCType.BIT, Boolean.valueOf(x), JavaType.BOOLEAN, forceEncrypt); + setValue(n, JDBCType.BIT, x, JavaType.BOOLEAN, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setBoolean"); } @@ -1374,7 +1377,7 @@ public final void setByte(int n, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setByte", new Object[] {n, x}); checkClosed(); - setValue(n, JDBCType.TINYINT, Byte.valueOf(x), JavaType.BYTE, false); + setValue(n, JDBCType.TINYINT, x, JavaType.BYTE, false); loggerExternal.exiting(getClassNameLogging(), "setByte"); } @@ -1399,7 +1402,7 @@ public final void setByte(int n, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setByte", new Object[] {n, x, forceEncrypt}); checkClosed(); - setValue(n, JDBCType.TINYINT, Byte.valueOf(x), JavaType.BYTE, forceEncrypt); + setValue(n, JDBCType.TINYINT, x, JavaType.BYTE, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setByte"); } @@ -1486,7 +1489,7 @@ public final void setDouble(int n, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDouble", new Object[] {n, x}); checkClosed(); - setValue(n, JDBCType.DOUBLE, Double.valueOf(x), JavaType.DOUBLE, false); + setValue(n, JDBCType.DOUBLE, x, JavaType.DOUBLE, false); loggerExternal.exiting(getClassNameLogging(), "setDouble"); } @@ -1511,7 +1514,7 @@ public final void setDouble(int n, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDouble", new Object[] {n, x, forceEncrypt}); checkClosed(); - setValue(n, JDBCType.DOUBLE, Double.valueOf(x), JavaType.DOUBLE, forceEncrypt); + setValue(n, JDBCType.DOUBLE, x, JavaType.DOUBLE, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setDouble"); } @@ -1520,7 +1523,7 @@ public final void setFloat(int n, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setFloat", new Object[] {n, x}); checkClosed(); - setValue(n, JDBCType.REAL, Float.valueOf(x), JavaType.FLOAT, false); + setValue(n, JDBCType.REAL, x, JavaType.FLOAT, false); loggerExternal.exiting(getClassNameLogging(), "setFloat"); } @@ -1545,7 +1548,7 @@ public final void setFloat(int n, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setFloat", new Object[] {n, x, forceEncrypt}); checkClosed(); - setValue(n, JDBCType.REAL, Float.valueOf(x), JavaType.FLOAT, forceEncrypt); + setValue(n, JDBCType.REAL, x, JavaType.FLOAT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setFloat"); } @@ -1554,7 +1557,7 @@ public final void setInt(int n, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setInt", new Object[] {n, value}); checkClosed(); - setValue(n, JDBCType.INTEGER, Integer.valueOf(value), JavaType.INTEGER, false); + setValue(n, JDBCType.INTEGER, value, JavaType.INTEGER, false); loggerExternal.exiting(getClassNameLogging(), "setInt"); } @@ -1579,7 +1582,7 @@ public final void setInt(int n, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setInt", new Object[] {n, value, forceEncrypt}); checkClosed(); - setValue(n, JDBCType.INTEGER, Integer.valueOf(value), JavaType.INTEGER, forceEncrypt); + setValue(n, JDBCType.INTEGER, value, JavaType.INTEGER, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setInt"); } @@ -1588,7 +1591,7 @@ public final void setLong(int n, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setLong", new Object[] {n, x}); checkClosed(); - setValue(n, JDBCType.BIGINT, Long.valueOf(x), JavaType.LONG, false); + setValue(n, JDBCType.BIGINT, x, JavaType.LONG, false); loggerExternal.exiting(getClassNameLogging(), "setLong"); } @@ -1613,7 +1616,7 @@ public final void setLong(int n, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setLong", new Object[] {n, x, forceEncrypt}); checkClosed(); - setValue(n, JDBCType.BIGINT, Long.valueOf(x), JavaType.LONG, forceEncrypt); + setValue(n, JDBCType.BIGINT, x, JavaType.LONG, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setLong"); } @@ -1703,7 +1706,7 @@ public final void setObject(int parameterIndex, setObject(setterGetParam(parameterIndex), x, JavaType.of(x), JDBCType.of(targetSqlType), (java.sql.Types.NUMERIC == targetSqlType || java.sql.Types.DECIMAL == targetSqlType || java.sql.Types.TIMESTAMP == targetSqlType || java.sql.Types.TIME == targetSqlType || microsoft.sql.Types.DATETIMEOFFSET == targetSqlType - || InputStream.class.isInstance(x) || Reader.class.isInstance(x)) ? Integer.valueOf(scaleOrLength) : null, + || InputStream.class.isInstance(x) || Reader.class.isInstance(x)) ? scaleOrLength : null, null, false, parameterIndex, null); loggerExternal.exiting(getClassNameLogging(), "setObject"); @@ -1753,7 +1756,7 @@ public final void setObject(int parameterIndex, setObject(setterGetParam(parameterIndex), x, JavaType.of(x), JDBCType.of(targetSqlType), (java.sql.Types.NUMERIC == targetSqlType || java.sql.Types.DECIMAL == targetSqlType - || InputStream.class.isInstance(x) || Reader.class.isInstance(x)) ? Integer.valueOf(scale) : null, + || InputStream.class.isInstance(x) || Reader.class.isInstance(x)) ? scale : null, precision, false, parameterIndex, null); loggerExternal.exiting(getClassNameLogging(), "setObject"); @@ -1809,7 +1812,7 @@ public final void setObject(int parameterIndex, setObject(setterGetParam(parameterIndex), x, JavaType.of(x), JDBCType.of(targetSqlType), (java.sql.Types.NUMERIC == targetSqlType || java.sql.Types.DECIMAL == targetSqlType - || InputStream.class.isInstance(x) || Reader.class.isInstance(x)) ? Integer.valueOf(scale) : null, + || InputStream.class.isInstance(x) || Reader.class.isInstance(x)) ? scale : null, precision, forceEncrypt, parameterIndex, null); loggerExternal.exiting(getClassNameLogging(), "setObject"); @@ -1880,7 +1883,7 @@ public final void setShort(int index, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setShort", new Object[] {index, x}); checkClosed(); - setValue(index, JDBCType.SMALLINT, Short.valueOf(x), JavaType.SHORT, false); + setValue(index, JDBCType.SMALLINT, x, JavaType.SHORT, false); loggerExternal.exiting(getClassNameLogging(), "setShort"); } @@ -1905,7 +1908,7 @@ public final void setShort(int index, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setShort", new Object[] {index, x, forceEncrypt}); checkClosed(); - setValue(index, JDBCType.SMALLINT, Short.valueOf(x), JavaType.SHORT, forceEncrypt); + setValue(index, JDBCType.SMALLINT, x, JavaType.SHORT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setShort"); } @@ -2584,10 +2587,9 @@ final void doExecutePreparedStatementBatch(PrepStmtBatchExecCmd batchCommand) th } // Retry execution if existing handle could not be re-used. - for(int attempt = 1; attempt <= 2; ++attempt) { - + for(int attempt = 1; attempt <= 2; ++attempt) { try { - + // Re-use handle if available, requires parameter definitions which are not available until here. if (reuseCachedHandle(hasNewTypeDefinitions, 1 < attempt)) { hasNewTypeDefinitions = false; @@ -2647,9 +2649,9 @@ final void doExecutePreparedStatementBatch(PrepStmtBatchExecCmd batchCommand) th // Retry if invalid handle exception. if (retryBasedOnFailedReuseOfCachedHandle(e, attempt)) { - // Reset number of batches prepared. + //reset number of batches prepare numBatchesPrepared = numBatchesExecuted; - retry = true; + retry = true; break; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement42Helper.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement42Helper.java index 702f3e2051..efd9c1774c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement42Helper.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement42Helper.java @@ -28,7 +28,7 @@ static final void setObject(SQLServerPreparedStatement ps, SQLServerStatement.loggerExternal.entering(ps.getClassNameLogging(), "setObject", new Object[] {index, obj, jdbcType}); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - ps.setObject(index, obj, jdbcType.getVendorTypeNumber().intValue()); + ps.setObject(index, obj, jdbcType.getVendorTypeNumber()); SQLServerStatement.loggerExternal.exiting(ps.getClassNameLogging(), "setObject"); } @@ -45,7 +45,7 @@ static final void setObject(SQLServerPreparedStatement ps, new Object[] {parameterIndex, x, targetSqlType, scaleOrLength}); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - ps.setObject(parameterIndex, x, targetSqlType.getVendorTypeNumber().intValue(), scaleOrLength); + ps.setObject(parameterIndex, x, targetSqlType.getVendorTypeNumber(), scaleOrLength); SQLServerStatement.loggerExternal.exiting(ps.getClassNameLogging(), "setObject"); } @@ -63,7 +63,7 @@ static final void setObject(SQLServerPreparedStatement ps, new Object[] {parameterIndex, x, targetSqlType, precision, scale}); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - ps.setObject(parameterIndex, x, targetSqlType.getVendorTypeNumber().intValue(), precision, scale, false); + ps.setObject(parameterIndex, x, targetSqlType.getVendorTypeNumber(), precision, scale, false); SQLServerStatement.loggerExternal.exiting(ps.getClassNameLogging(), "setObject"); } @@ -82,7 +82,7 @@ static final void setObject(SQLServerPreparedStatement ps, new Object[] {parameterIndex, x, targetSqlType, precision, scale, forceEncrypt}); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - ps.setObject(parameterIndex, x, targetSqlType.getVendorTypeNumber().intValue(), precision, scale, forceEncrypt); + ps.setObject(parameterIndex, x, targetSqlType.getVendorTypeNumber(), precision, scale, forceEncrypt); SQLServerStatement.loggerExternal.exiting(ps.getClassNameLogging(), "setObject"); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 4386a6c5c7..3945a2096c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -33,7 +33,7 @@ protected Object[][] getContents() { {"R_dbMirroringWithMultiSubnetFailover", "Connecting to a mirrored SQL Server instance using the multiSubnetFailover connection property is not supported."}, {"R_dbMirroringWithReadOnlyIntent", "Connecting to a mirrored SQL Server instance using the ApplicationIntent ReadOnly connection property is not supported."}, {"R_ipAddressLimitWithMultiSubnetFailover", "Connecting with the multiSubnetFailover connection property to a SQL Server instance configured with more than {0} IP addresses is not supported."}, - {"R_connectionTimedOut", "Connection timed out: no further information"}, + {"R_connectionTimedOut", "Connection timed out: no further information."}, {"R_invalidPositionIndex", "The position index {0} is not valid."}, {"R_invalidLength", "The length {0} is not valid."}, {"R_unknownSSType", "Invalid SQL Server data type {0}."}, @@ -96,7 +96,6 @@ protected Object[][] getContents() { {"R_noColumnParameterValue", "No column parameter values were specified to update the row."}, {"R_statementMustBeExecuted", "The statement must be executed before any results can be obtained."}, {"R_modeSuppliedNotValid", "The supplied mode is not valid."}, - {"R_variantNotSupported", "The \"variant\" data type is not supported."}, {"R_errorConnectionString", "The connection string contains a badly formed name or value."}, {"R_errorProcessingComplexQuery", "An error occurred while processing the complex query."}, {"R_invalidOffset", "The offset {0} is not valid."}, @@ -104,6 +103,7 @@ protected Object[][] getContents() { {"R_invalidConnection", "The connection URL is invalid."}, {"R_cannotTakeArgumentsPreparedOrCallable", "The method {0} cannot take arguments on a PreparedStatement or CallableStatement."}, {"R_unsupportedConversionFromTo", "The conversion from {0} to {1} is unsupported."}, // Invalid conversion (e.g. MONEY to Timestamp) + {"R_unsupportedConversionTo", "The conversion to {0} is unsupported."}, // Invalid conversion to an unknown type {"R_errorConvertingValue","An error occurred while converting the {0} value to JDBC data type {1}."}, // Data-dependent conversion failure (e.g. "foo" vs. "123", to Integer) {"R_streamIsClosed", "The stream is closed."}, {"R_invalidTDS", "The TDS protocol stream is not valid."}, @@ -190,7 +190,7 @@ protected Object[][] getContents() { {"R_socketTimeoutPropertyDescription", "The number of milliseconds to wait before the java.net.SocketTimeoutException is raised."}, {"R_serverPreparedStatementDiscardThresholdPropertyDescription", "The threshold for when to close discarded prepare statements on the server (calling a batch of sp_unprepares). A value of 1 or less will cause sp_unprepare to be called immediately on PreparedStatment close."}, {"R_enablePrepareOnFirstPreparedStatementCallPropertyDescription", "This setting specifies whether a prepared statement is prepared (sp_prepexec) on first use (property=true) or on second after first calling sp_executesql (property=false)."}, - {"R_statementPoolingCacheSizePropertyDescription", "This setting specifies the size of the prepared statement cache for a conection. A value less than 1 means no cache."}, + {"R_statementPoolingCacheSizePropertyDescription", "This setting specifies the size of the prepared statement cache for a conection. A value less than 1 means no cache."}, {"R_gsscredentialPropertyDescription", "Impersonated GSS Credential to access SQL Server."}, {"R_noParserSupport", "An error occurred while instantiating the required parser. Error: \"{0}\""}, {"R_writeOnlyXML", "Cannot read from this SQLXML instance. This instance is for writing data only."}, @@ -203,7 +203,7 @@ protected Object[][] getContents() { {"R_isFreed", "This {0} object has been freed. It can no longer be accessed."}, {"R_invalidProperty", "This property is not supported: {0}." }, {"R_referencingFailedTSP", "The DataSource trustStore password needs to be set." }, - {"R_valueOutOfRange", "One or more values is out of range of values for the {0} SQL Server data type" }, + {"R_valueOutOfRange", "One or more values is out of range of values for the {0} SQL Server data type." }, {"R_integratedAuthenticationFailed", "Integrated authentication failed."}, {"R_permissionDenied", "Security violation. Permission to target \"{0}\" denied."}, {"R_getSchemaError", "Error getting default schema name."}, @@ -372,7 +372,7 @@ protected Object[][] getContents() { {"R_invalidKeyStoreFile", "Cannot parse \"{0}\". Either the file format is not valid or the password is not correct."}, // for JKS/PKCS {"R_invalidCEKCacheTtl", "Invalid column encryption key cache time-to-live specified. The columnEncryptionKeyCacheTtl value cannot be negative and timeUnit can only be DAYS, HOURS, MINUTES or SECONDS."}, {"R_sendTimeAsDateTimeForAE", "Use sendTimeAsDateTime=false with Always Encrypted."}, - {"R_TVPnotWorkWithSetObjectResultSet" , "setObject() with ResultSet is not supported for Table-Valued Parameter. Please use setStructured()"}, + {"R_TVPnotWorkWithSetObjectResultSet" , "setObject() with ResultSet is not supported for Table-Valued Parameter. Please use setStructured()."}, {"R_invalidQueryTimeout", "The queryTimeout {0} is not valid."}, {"R_invalidSocketTimeout", "The socketTimeout {0} is not valid."}, {"R_fipsPropertyDescription", "Determines if enable FIPS compilant SSL connection between the client and the server."}, @@ -385,5 +385,10 @@ protected Object[][] getContents() { {"R_kerberosLoginFailed", "Kerberos Login failed: {0} due to {1} ({2})"}, {"R_StoredProcedureNotFound", "Could not find stored procedure ''{0}''."}, {"R_jaasConfigurationNamePropertyDescription", "Login configuration file for Kerberos authentication."}, + {"R_AKVKeyNotFound", "Key not found: {0}"}, + {"R_SQLVariantSupport", "SQL_VARIANT datatype is not supported in pre-SQL 2008 version."}, + {"R_invalidProbbytes", "SQL_VARIANT: invalid probBytes for {0} type."}, + {"R_invalidStringValue", "SQL_VARIANT does not support string values more than 8000 length."}, + {"R_invalidValueForTVPWithSQLVariant", "Inserting null value with column type sql_variant in TVP is not supported."}, }; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java index 98da7f11d0..936e4727e4 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java @@ -27,6 +27,7 @@ import java.sql.SQLXML; import java.text.MessageFormat; import java.util.Calendar; +import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; @@ -395,7 +396,7 @@ boolean onDone(TDSReader tdsReader) throws SQLServerException { public boolean isWrapperFor(Class iface) throws SQLException { loggerExternal.entering(getClassNameLogging(), "isWrapperFor"); boolean f = iface.isInstance(this); - loggerExternal.exiting(getClassNameLogging(), "isWrapperFor", Boolean.valueOf(f)); + loggerExternal.exiting(getClassNameLogging(), "isWrapperFor", f); return f; } @@ -539,7 +540,7 @@ private void verifyValidColumnIndex(int index) throws SQLServerException { if (index < 1 || index > nCols) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_indexOutOfRange")); - Object[] msgArgs = {new Integer(index)}; + Object[] msgArgs = {index}; SQLServerException.makeFromDriverError(stmt.connection, stmt, form.format(msgArgs), "07009", false); } } @@ -1818,7 +1819,7 @@ public void setFetchDirection(int direction) throws SQLServerException { (ResultSet.FETCH_FORWARD != direction && (SQLServerResultSet.TYPE_SS_DIRECT_FORWARD_ONLY == stmt.resultSetType || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == stmt.resultSetType))) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidFetchDirection")); - Object[] msgArgs = {new Integer(direction)}; + Object[] msgArgs = {direction}; SQLServerException.makeFromDriverError(stmt.connection, stmt, form.format(msgArgs), null, false); } @@ -1926,7 +1927,15 @@ private Object getValue(int columnIndex, lastValueWasNull = (null == o); return o; } - + + void setInternalVariantType(int columnIndex, SqlVariant type) throws SQLServerException{ + getterGetColumn(columnIndex).setInternalVariant(type); + } + + SqlVariant getVariantInternalType(int columnIndex) throws SQLServerException { + return getterGetColumn(columnIndex).getInternalVariant(); + } + private Object getStream(int columnIndex, StreamType streamType) throws SQLServerException { Object value = getValue(columnIndex, streamType.getJDBCType(), @@ -2008,7 +2017,7 @@ public boolean getBoolean(int columnIndex) throws SQLServerException { checkClosed(); Boolean value = (Boolean) getValue(columnIndex, JDBCType.BIT); loggerExternal.exiting(getClassNameLogging(), "getBoolean", value); - return null != value ? value.booleanValue() : false; + return null != value ? value : false; } public boolean getBoolean(String columnName) throws SQLServerException { @@ -2016,7 +2025,7 @@ public boolean getBoolean(String columnName) throws SQLServerException { checkClosed(); Boolean value = (Boolean) getValue(findColumn(columnName), JDBCType.BIT); loggerExternal.exiting(getClassNameLogging(), "getBoolean", value); - return null != value ? value.booleanValue() : false; + return null != value ? value : false; } public byte getByte(int columnIndex) throws SQLServerException { @@ -2092,7 +2101,7 @@ public double getDouble(int columnIndex) throws SQLServerException { checkClosed(); Double value = (Double) getValue(columnIndex, JDBCType.DOUBLE); loggerExternal.exiting(getClassNameLogging(), "getDouble", value); - return null != value ? value.doubleValue() : 0; + return null != value ? value : 0; } public double getDouble(String columnName) throws SQLServerException { @@ -2100,7 +2109,7 @@ public double getDouble(String columnName) throws SQLServerException { checkClosed(); Double value = (Double) getValue(findColumn(columnName), JDBCType.DOUBLE); loggerExternal.exiting(getClassNameLogging(), "getDouble", value); - return null != value ? value.doubleValue() : 0; + return null != value ? value : 0; } public float getFloat(int columnIndex) throws SQLServerException { @@ -2108,7 +2117,7 @@ public float getFloat(int columnIndex) throws SQLServerException { checkClosed(); Float value = (Float) getValue(columnIndex, JDBCType.REAL); loggerExternal.exiting(getClassNameLogging(), "getFloat", value); - return null != value ? value.floatValue() : 0; + return null != value ? value : 0; } public float getFloat(String columnName) throws SQLServerException { @@ -2116,7 +2125,7 @@ public float getFloat(String columnName) throws SQLServerException { checkClosed(); Float value = (Float) getValue(findColumn(columnName), JDBCType.REAL); loggerExternal.exiting(getClassNameLogging(), "getFloat", value); - return null != value ? value.floatValue() : 0; + return null != value ? value : 0; } public int getInt(int columnIndex) throws SQLServerException { @@ -2124,7 +2133,7 @@ public int getInt(int columnIndex) throws SQLServerException { checkClosed(); Integer value = (Integer) getValue(columnIndex, JDBCType.INTEGER); loggerExternal.exiting(getClassNameLogging(), "getInt", value); - return null != value ? value.intValue() : 0; + return null != value ? value : 0; } public int getInt(String columnName) throws SQLServerException { @@ -2132,7 +2141,7 @@ public int getInt(String columnName) throws SQLServerException { checkClosed(); Integer value = (Integer) getValue(findColumn(columnName), JDBCType.INTEGER); loggerExternal.exiting(getClassNameLogging(), "getInt", value); - return null != value ? value.intValue() : 0; + return null != value ? value : 0; } public long getLong(int columnIndex) throws SQLServerException { @@ -2140,7 +2149,7 @@ public long getLong(int columnIndex) throws SQLServerException { checkClosed(); Long value = (Long) getValue(columnIndex, JDBCType.BIGINT); loggerExternal.exiting(getClassNameLogging(), "getLong", value); - return null != value ? value.longValue() : 0; + return null != value ? value : 0; } public long getLong(String columnName) throws SQLServerException { @@ -2148,7 +2157,7 @@ public long getLong(String columnName) throws SQLServerException { checkClosed(); Long value = (Long) getValue(findColumn(columnName), JDBCType.BIGINT); loggerExternal.exiting(getClassNameLogging(), "getLong", value); - return null != value ? value.longValue() : 0; + return null != value ? value : 0; } public java.sql.ResultSetMetaData getMetaData() throws SQLServerException { @@ -2170,8 +2179,84 @@ public Object getObject(int columnIndex) throws SQLServerException { public T getObject(int columnIndex, Class type) throws SQLException { - // The driver currently does not implement the optional JDBC APIs - throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + loggerExternal.entering(getClassNameLogging(), "getObject", columnIndex); + checkClosed(); + Object returnValue; + if (type == String.class) { + returnValue = getString(columnIndex); + } + else if (type == Byte.class) { + byte byteValue = getByte(columnIndex); + returnValue = wasNull() ? null : byteValue; + } + else if (type == Short.class) { + short shortValue = getShort(columnIndex); + returnValue = wasNull() ? null : shortValue; + } + else if (type == Integer.class) { + int intValue = getInt(columnIndex); + returnValue = wasNull() ? null : intValue; + } + else if (type == Long.class) { + long longValue = getLong(columnIndex); + returnValue = wasNull() ? null : longValue; + } + else if (type == BigDecimal.class) { + returnValue = getBigDecimal(columnIndex); + } + else if (type == Boolean.class) { + boolean booleanValue = getBoolean(columnIndex); + returnValue = wasNull() ? null : booleanValue; + } + else if (type == java.sql.Date.class) { + returnValue = getDate(columnIndex); + } + else if (type == java.sql.Time.class) { + returnValue = getTime(columnIndex); + } + else if (type == java.sql.Timestamp.class) { + returnValue = getTimestamp(columnIndex); + } + else if (type == microsoft.sql.DateTimeOffset.class) { + returnValue = getDateTimeOffset(columnIndex); + } + else if (type == UUID.class) { + // read binary, avoid string allocation and parsing + byte[] guid = getBytes(columnIndex); + returnValue = guid != null ? Util.readGUIDtoUUID(guid) : null; + } + else if (type == SQLXML.class) { + returnValue = getSQLXML(columnIndex); + } + else if (type == Blob.class) { + returnValue = getBlob(columnIndex); + } + else if (type == Clob.class) { + returnValue = getClob(columnIndex); + } + else if (type == NClob.class) { + returnValue = getNClob(columnIndex); + } + else if (type == byte[].class) { + returnValue = getBytes(columnIndex); + } + else if (type == Float.class) { + float floatValue = getFloat(columnIndex); + returnValue = wasNull() ? null : floatValue; + } + else if (type == Double.class) { + double doubleValue = getDouble(columnIndex); + returnValue = wasNull() ? null : doubleValue; + } + else { + // if the type is not supported the specification says the should + // a SQLException instead of SQLFeatureNotSupportedException + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unsupportedConversionTo")); + Object[] msgArgs = {type}; + throw new SQLServerException(form.format(msgArgs), SQLState.DATA_EXCEPTION_NOT_SPECIFIC, DriverError.NOT_SET, null); + } + loggerExternal.exiting(getClassNameLogging(), "getObject", columnIndex); + return type.cast(returnValue); } public Object getObject(String columnName) throws SQLServerException { @@ -2184,8 +2269,11 @@ public Object getObject(String columnName) throws SQLServerException { public T getObject(String columnName, Class type) throws SQLException { - // The driver currently does not implement the optional JDBC APIs - throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); + loggerExternal.entering(getClassNameLogging(), "getObject", columnName); + checkClosed(); + T value = getObject(findColumn(columnName), type); + loggerExternal.exiting(getClassNameLogging(), "getObject", value); + return value; } public short getShort(int columnIndex) throws SQLServerException { @@ -2193,7 +2281,7 @@ public short getShort(int columnIndex) throws SQLServerException { checkClosed(); Short value = (Short) getValue(columnIndex, JDBCType.SMALLINT); loggerExternal.exiting(getClassNameLogging(), "getShort", value); - return null != value ? value.shortValue() : 0; + return null != value ? value : 0; } public short getShort(String columnName) throws SQLServerException { @@ -2201,7 +2289,7 @@ public short getShort(String columnName) throws SQLServerException { checkClosed(); Short value = (Short) getValue(findColumn(columnName), JDBCType.SMALLINT); loggerExternal.exiting(getClassNameLogging(), "getShort", value); - return null != value ? value.shortValue() : 0; + return null != value ? value : 0; } public String getString(int columnIndex) throws SQLServerException { @@ -2946,7 +3034,7 @@ public void updateBoolean(int index, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateBoolean", new Object[] {index, x}); checkClosed(); - updateValue(index, JDBCType.BIT, Boolean.valueOf(x), JavaType.BOOLEAN, false); + updateValue(index, JDBCType.BIT, x, JavaType.BOOLEAN, false); loggerExternal.exiting(getClassNameLogging(), "updateBoolean"); } @@ -2973,7 +3061,7 @@ public void updateBoolean(int index, if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateBoolean", new Object[] {index, x, forceEncrypt}); checkClosed(); - updateValue(index, JDBCType.BIT, Boolean.valueOf(x), JavaType.BOOLEAN, forceEncrypt); + updateValue(index, JDBCType.BIT, x, JavaType.BOOLEAN, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateBoolean"); } @@ -2984,7 +3072,7 @@ public void updateByte(int index, loggerExternal.entering(getClassNameLogging(), "updateByte", new Object[] {index, x}); checkClosed(); - updateValue(index, JDBCType.TINYINT, Byte.valueOf(x), JavaType.BYTE, false); + updateValue(index, JDBCType.TINYINT, x, JavaType.BYTE, false); loggerExternal.exiting(getClassNameLogging(), "updateByte"); } @@ -3012,7 +3100,7 @@ public void updateByte(int index, loggerExternal.entering(getClassNameLogging(), "updateByte", new Object[] {index, x, forceEncrypt}); checkClosed(); - updateValue(index, JDBCType.TINYINT, Byte.valueOf(x), JavaType.BYTE, forceEncrypt); + updateValue(index, JDBCType.TINYINT, x, JavaType.BYTE, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateByte"); } @@ -3023,7 +3111,7 @@ public void updateShort(int index, loggerExternal.entering(getClassNameLogging(), "updateShort", new Object[] {index, x}); checkClosed(); - updateValue(index, JDBCType.SMALLINT, Short.valueOf(x), JavaType.SHORT, false); + updateValue(index, JDBCType.SMALLINT, x, JavaType.SHORT, false); loggerExternal.exiting(getClassNameLogging(), "updateShort"); } @@ -3051,7 +3139,7 @@ public void updateShort(int index, loggerExternal.entering(getClassNameLogging(), "updateShort", new Object[] {index, x, forceEncrypt}); checkClosed(); - updateValue(index, JDBCType.SMALLINT, Short.valueOf(x), JavaType.SHORT, forceEncrypt); + updateValue(index, JDBCType.SMALLINT, x, JavaType.SHORT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateShort"); } @@ -3062,7 +3150,7 @@ public void updateInt(int index, loggerExternal.entering(getClassNameLogging(), "updateInt", new Object[] {index, x}); checkClosed(); - updateValue(index, JDBCType.INTEGER, Integer.valueOf(x), JavaType.INTEGER, false); + updateValue(index, JDBCType.INTEGER, x, JavaType.INTEGER, false); loggerExternal.exiting(getClassNameLogging(), "updateInt"); } @@ -3090,7 +3178,7 @@ public void updateInt(int index, loggerExternal.entering(getClassNameLogging(), "updateInt", new Object[] {index, x, forceEncrypt}); checkClosed(); - updateValue(index, JDBCType.INTEGER, Integer.valueOf(x), JavaType.INTEGER, forceEncrypt); + updateValue(index, JDBCType.INTEGER, x, JavaType.INTEGER, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateInt"); } @@ -3101,7 +3189,7 @@ public void updateLong(int index, loggerExternal.entering(getClassNameLogging(), "updateLong", new Object[] {index, x}); checkClosed(); - updateValue(index, JDBCType.BIGINT, Long.valueOf(x), JavaType.LONG, false); + updateValue(index, JDBCType.BIGINT, x, JavaType.LONG, false); loggerExternal.exiting(getClassNameLogging(), "updateLong"); } @@ -3129,7 +3217,7 @@ public void updateLong(int index, loggerExternal.entering(getClassNameLogging(), "updateLong", new Object[] {index, x, forceEncrypt}); checkClosed(); - updateValue(index, JDBCType.BIGINT, Long.valueOf(x), JavaType.LONG, forceEncrypt); + updateValue(index, JDBCType.BIGINT, x, JavaType.LONG, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateLong"); } @@ -3140,7 +3228,7 @@ public void updateFloat(int index, loggerExternal.entering(getClassNameLogging(), "updateFloat", new Object[] {index, x}); checkClosed(); - updateValue(index, JDBCType.REAL, Float.valueOf(x), JavaType.FLOAT, false); + updateValue(index, JDBCType.REAL, x, JavaType.FLOAT, false); loggerExternal.exiting(getClassNameLogging(), "updateFloat"); } @@ -3168,7 +3256,7 @@ public void updateFloat(int index, loggerExternal.entering(getClassNameLogging(), "updateFloat", new Object[] {index, x, forceEncrypt}); checkClosed(); - updateValue(index, JDBCType.REAL, Float.valueOf(x), JavaType.FLOAT, forceEncrypt); + updateValue(index, JDBCType.REAL, x, JavaType.FLOAT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateFloat"); } @@ -3179,7 +3267,7 @@ public void updateDouble(int index, loggerExternal.entering(getClassNameLogging(), "updateDouble", new Object[] {index, x}); checkClosed(); - updateValue(index, JDBCType.DOUBLE, Double.valueOf(x), JavaType.DOUBLE, false); + updateValue(index, JDBCType.DOUBLE, x, JavaType.DOUBLE, false); loggerExternal.exiting(getClassNameLogging(), "updateDouble"); } @@ -3207,7 +3295,7 @@ public void updateDouble(int index, loggerExternal.entering(getClassNameLogging(), "updateDouble", new Object[] {index, x, forceEncrypt}); checkClosed(); - updateValue(index, JDBCType.DOUBLE, Double.valueOf(x), JavaType.DOUBLE, forceEncrypt); + updateValue(index, JDBCType.DOUBLE, x, JavaType.DOUBLE, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateDouble"); } @@ -4364,7 +4452,7 @@ public void updateObject(int index, loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {index, x, scale}); checkClosed(); - updateObject(index, x, Integer.valueOf(scale), null, null, false); + updateObject(index, x, scale, null, null, false); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @@ -4394,7 +4482,7 @@ public void updateObject(int index, loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {index, x, scale}); checkClosed(); - updateObject(index, x, Integer.valueOf(scale), null, precision, false); + updateObject(index, x, scale, null, precision, false); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @@ -4429,7 +4517,7 @@ public void updateObject(int index, loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {index, x, scale, forceEncrypt}); checkClosed(); - updateObject(index, x, Integer.valueOf(scale), null, precision, forceEncrypt); + updateObject(index, x, scale, null, precision, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @@ -4507,7 +4595,7 @@ public void updateBoolean(String columnName, loggerExternal.entering(getClassNameLogging(), "updateBoolean", new Object[] {columnName, x}); checkClosed(); - updateValue(findColumn(columnName), JDBCType.BIT, Boolean.valueOf(x), JavaType.BOOLEAN, false); + updateValue(findColumn(columnName), JDBCType.BIT, x, JavaType.BOOLEAN, false); loggerExternal.exiting(getClassNameLogging(), "updateBoolean"); } @@ -4535,7 +4623,7 @@ public void updateBoolean(String columnName, loggerExternal.entering(getClassNameLogging(), "updateBoolean", new Object[] {columnName, x, forceEncrypt}); checkClosed(); - updateValue(findColumn(columnName), JDBCType.BIT, Boolean.valueOf(x), JavaType.BOOLEAN, forceEncrypt); + updateValue(findColumn(columnName), JDBCType.BIT, x, JavaType.BOOLEAN, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateBoolean"); } @@ -4586,7 +4674,7 @@ public void updateShort(String columnName, loggerExternal.entering(getClassNameLogging(), "updateShort", new Object[] {columnName, x}); checkClosed(); - updateValue(findColumn(columnName), JDBCType.SMALLINT, Short.valueOf(x), JavaType.SHORT, false); + updateValue(findColumn(columnName), JDBCType.SMALLINT, x, JavaType.SHORT, false); loggerExternal.exiting(getClassNameLogging(), "updateShort"); } @@ -4614,7 +4702,7 @@ public void updateShort(String columnName, loggerExternal.entering(getClassNameLogging(), "updateShort", new Object[] {columnName, x, forceEncrypt}); checkClosed(); - updateValue(findColumn(columnName), JDBCType.SMALLINT, Short.valueOf(x), JavaType.SHORT, forceEncrypt); + updateValue(findColumn(columnName), JDBCType.SMALLINT, x, JavaType.SHORT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateShort"); } @@ -4625,7 +4713,7 @@ public void updateInt(String columnName, loggerExternal.entering(getClassNameLogging(), "updateInt", new Object[] {columnName, x}); checkClosed(); - updateValue(findColumn(columnName), JDBCType.INTEGER, Integer.valueOf(x), JavaType.INTEGER, false); + updateValue(findColumn(columnName), JDBCType.INTEGER, x, JavaType.INTEGER, false); loggerExternal.exiting(getClassNameLogging(), "updateInt"); } @@ -4653,7 +4741,7 @@ public void updateInt(String columnName, loggerExternal.entering(getClassNameLogging(), "updateInt", new Object[] {columnName, x, forceEncrypt}); checkClosed(); - updateValue(findColumn(columnName), JDBCType.INTEGER, Integer.valueOf(x), JavaType.INTEGER, forceEncrypt); + updateValue(findColumn(columnName), JDBCType.INTEGER, x, JavaType.INTEGER, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateInt"); } @@ -4664,7 +4752,7 @@ public void updateLong(String columnName, loggerExternal.entering(getClassNameLogging(), "updateLong", new Object[] {columnName, x}); checkClosed(); - updateValue(findColumn(columnName), JDBCType.BIGINT, Long.valueOf(x), JavaType.LONG, false); + updateValue(findColumn(columnName), JDBCType.BIGINT, x, JavaType.LONG, false); loggerExternal.exiting(getClassNameLogging(), "updateLong"); } @@ -4692,7 +4780,7 @@ public void updateLong(String columnName, loggerExternal.entering(getClassNameLogging(), "updateLong", new Object[] {columnName, x, forceEncrypt}); checkClosed(); - updateValue(findColumn(columnName), JDBCType.BIGINT, Long.valueOf(x), JavaType.LONG, forceEncrypt); + updateValue(findColumn(columnName), JDBCType.BIGINT, x, JavaType.LONG, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateLong"); } @@ -4703,7 +4791,7 @@ public void updateFloat(String columnName, loggerExternal.entering(getClassNameLogging(), "updateFloat", new Object[] {columnName, x}); checkClosed(); - updateValue(findColumn(columnName), JDBCType.REAL, Float.valueOf(x), JavaType.FLOAT, false); + updateValue(findColumn(columnName), JDBCType.REAL, x, JavaType.FLOAT, false); loggerExternal.exiting(getClassNameLogging(), "updateFloat"); } @@ -4731,7 +4819,7 @@ public void updateFloat(String columnName, loggerExternal.entering(getClassNameLogging(), "updateFloat", new Object[] {columnName, x, forceEncrypt}); checkClosed(); - updateValue(findColumn(columnName), JDBCType.REAL, Float.valueOf(x), JavaType.FLOAT, forceEncrypt); + updateValue(findColumn(columnName), JDBCType.REAL, x, JavaType.FLOAT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateFloat"); } @@ -4742,7 +4830,7 @@ public void updateDouble(String columnName, loggerExternal.entering(getClassNameLogging(), "updateDouble", new Object[] {columnName, x}); checkClosed(); - updateValue(findColumn(columnName), JDBCType.DOUBLE, Double.valueOf(x), JavaType.DOUBLE, false); + updateValue(findColumn(columnName), JDBCType.DOUBLE, x, JavaType.DOUBLE, false); loggerExternal.exiting(getClassNameLogging(), "updateDouble"); } @@ -4770,7 +4858,7 @@ public void updateDouble(String columnName, loggerExternal.entering(getClassNameLogging(), "updateDouble", new Object[] {columnName, x, forceEncrypt}); checkClosed(); - updateValue(findColumn(columnName), JDBCType.DOUBLE, Double.valueOf(x), JavaType.DOUBLE, forceEncrypt); + updateValue(findColumn(columnName), JDBCType.DOUBLE, x, JavaType.DOUBLE, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateDouble"); } @@ -5415,7 +5503,7 @@ public void updateObject(String columnName, loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {columnName, x, scale}); checkClosed(); - updateObject(findColumn(columnName), x, Integer.valueOf(scale), null, null, false); + updateObject(findColumn(columnName), x, scale, null, null, false); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @@ -5445,7 +5533,7 @@ public void updateObject(String columnName, loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {columnName, x, precision, scale}); checkClosed(); - updateObject(findColumn(columnName), x, Integer.valueOf(scale), null, precision, false); + updateObject(findColumn(columnName), x, scale, null, precision, false); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @@ -5480,7 +5568,7 @@ public void updateObject(String columnName, loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {columnName, x, precision, scale, forceEncrypt}); checkClosed(); - updateObject(findColumn(columnName), x, Integer.valueOf(scale), null, precision, forceEncrypt); + updateObject(findColumn(columnName), x, scale, null, precision, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @@ -5631,9 +5719,9 @@ private void doInsertRowRPC(TDSCommand command, tdsWriter.writeShort(TDS.PROCID_SP_CURSOR); tdsWriter.writeByte((byte) 0); // RPC procedure option 1 tdsWriter.writeByte((byte) 0); // RPC procedure option 2 - tdsWriter.writeRPCInt(null, new Integer(serverCursorId), false); - tdsWriter.writeRPCInt(null, new Integer(TDS.SP_CURSOR_OP_INSERT), false); - tdsWriter.writeRPCInt(null, new Integer(fetchBufferGetRow()), false); + tdsWriter.writeRPCInt(null, serverCursorId, false); + tdsWriter.writeRPCInt(null, (int) TDS.SP_CURSOR_OP_INSERT, false); + tdsWriter.writeRPCInt(null, fetchBufferGetRow(), false); if (hasUpdatedColumns()) { tdsWriter.writeRPCStringUnicode(tableName); @@ -5706,9 +5794,9 @@ private void doUpdateRowRPC(TDSCommand command) throws SQLServerException { tdsWriter.writeShort(TDS.PROCID_SP_CURSOR); tdsWriter.writeByte((byte) 0); // RPC procedure option 1 tdsWriter.writeByte((byte) 0); // RPC procedure option 2 - tdsWriter.writeRPCInt(null, new Integer(serverCursorId), false); - tdsWriter.writeRPCInt(null, new Integer(TDS.SP_CURSOR_OP_UPDATE | TDS.SP_CURSOR_OP_SETPOSITION), false); - tdsWriter.writeRPCInt(null, new Integer(fetchBufferGetRow()), false); + tdsWriter.writeRPCInt(null, serverCursorId, false); + tdsWriter.writeRPCInt(null, TDS.SP_CURSOR_OP_UPDATE | TDS.SP_CURSOR_OP_SETPOSITION, false); + tdsWriter.writeRPCInt(null, fetchBufferGetRow(), false); tdsWriter.writeRPCStringUnicode(""); assert hasUpdatedColumns(); @@ -5779,9 +5867,9 @@ private void doDeleteRowRPC(TDSCommand command) throws SQLServerException { tdsWriter.writeShort(TDS.PROCID_SP_CURSOR); tdsWriter.writeByte((byte) 0); // RPC procedure option 1 tdsWriter.writeByte((byte) 0); // RPC procedure option 2 - tdsWriter.writeRPCInt(null, new Integer(serverCursorId), false); - tdsWriter.writeRPCInt(null, new Integer(TDS.SP_CURSOR_OP_DELETE | TDS.SP_CURSOR_OP_SETPOSITION), false); - tdsWriter.writeRPCInt(null, new Integer(fetchBufferGetRow()), false); + tdsWriter.writeRPCInt(null, serverCursorId, false); + tdsWriter.writeRPCInt(null, TDS.SP_CURSOR_OP_DELETE | TDS.SP_CURSOR_OP_SETPOSITION, false); + tdsWriter.writeRPCInt(null, fetchBufferGetRow(), false); tdsWriter.writeRPCStringUnicode(""); TDSParser.parse(command.startResponse(), command.getLogContext()); @@ -6337,10 +6425,10 @@ final boolean doExecute() throws SQLServerException { tdsWriter.writeShort(TDS.PROCID_SP_CURSORFETCH); tdsWriter.writeByte(TDS.RPC_OPTION_NO_METADATA); tdsWriter.writeByte((byte) 0); // RPC procedure option 2 - tdsWriter.writeRPCInt(null, new Integer(serverCursorId), false); - tdsWriter.writeRPCInt(null, new Integer(fetchType), false); - tdsWriter.writeRPCInt(null, new Integer(startRow), false); - tdsWriter.writeRPCInt(null, new Integer(numRows), false); + tdsWriter.writeRPCInt(null, serverCursorId, false); + tdsWriter.writeRPCInt(null, fetchType, false); + tdsWriter.writeRPCInt(null, startRow, false); + tdsWriter.writeRPCInt(null, numRows, false); // To free up the thread on the server that is feeding us these results, // read the entire response off the wire UNLESS this is a forward only @@ -6489,7 +6577,7 @@ final boolean doExecute() throws SQLServerException { tdsWriter.writeShort(TDS.PROCID_SP_CURSORCLOSE); tdsWriter.writeByte((byte) 0); // RPC procedure option 1 tdsWriter.writeByte((byte) 0); // RPC procedure option 2 - tdsWriter.writeRPCInt(null, new Integer(serverCursorId), false); + tdsWriter.writeRPCInt(null, serverCursorId, false); TDSParser.parse(startResponse(), getLogContext()); return true; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet42.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet42.java index a780e7e39c..cc900e65f3 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet42.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet42.java @@ -57,7 +57,7 @@ public void updateObject(int index, checkClosed(); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - updateObject(index, obj, Integer.valueOf(scale), JDBCType.of(targetSqlType.getVendorTypeNumber()), null, false); + updateObject(index, obj, scale, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, false); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @@ -74,7 +74,7 @@ public void updateObject(int index, checkClosed(); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - updateObject(index, obj, Integer.valueOf(scale), JDBCType.of(targetSqlType.getVendorTypeNumber()), null, forceEncrypt); + updateObject(index, obj, scale, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @@ -91,7 +91,7 @@ public void updateObject(String columnName, checkClosed(); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - updateObject(findColumn(columnName), obj, Integer.valueOf(scale), JDBCType.of(targetSqlType.getVendorTypeNumber()), null, false); + updateObject(findColumn(columnName), obj, scale, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, false); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @@ -109,7 +109,7 @@ public void updateObject(String columnName, checkClosed(); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types - updateObject(findColumn(columnName), obj, Integer.valueOf(scale), JDBCType.of(targetSqlType.getVendorTypeNumber()), null, forceEncrypt); + updateObject(findColumn(columnName), obj, scale, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSetMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSetMetaData.java index 7efb83bad1..693f3fe0cd 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSetMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSetMetaData.java @@ -120,8 +120,12 @@ public int getColumnType(int column) throws SQLServerException { if (null != cryptoMetadata) { typeInfo = cryptoMetadata.getBaseTypeInfo(); } - + JDBCType jdbcType = typeInfo.getSSType().getJDBCType(); + // in bulkcopy for instance, we need to return the real jdbc type which is sql variant and not the default Char one. + if ( SSType.SQL_VARIANT == typeInfo.getSSType()){ + jdbcType = JDBCType.SQL_VARIANT; + } int r = jdbcType.asJavaSqlType(); if (con.isKatmaiOrLater()) { SSType sqlType = typeInfo.getSSType(); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index 3ae799ab92..632edf9e64 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -688,7 +688,7 @@ public int executeUpdate(String sql) throws SQLServerException { if (updateCount < Integer.MIN_VALUE || updateCount > Integer.MAX_VALUE) SQLServerException.makeFromDriverError(connection, this, SQLServerException.getErrString("R_updateCountOutofRange"), null, true); - loggerExternal.exiting(getClassNameLogging(), "executeUpdate", new Long(updateCount)); + loggerExternal.exiting(getClassNameLogging(), "executeUpdate", updateCount); return (int) updateCount; } @@ -712,7 +712,7 @@ public long executeLargeUpdate(String sql) throws SQLServerException { checkClosed(); executeStatement(new StmtExecCmd(this, sql, EXECUTE_UPDATE, NO_GENERATED_KEYS)); - loggerExternal.exiting(getClassNameLogging(), "executeLargeUpdate", new Long(updateCount)); + loggerExternal.exiting(getClassNameLogging(), "executeLargeUpdate", updateCount); return updateCount; } @@ -732,7 +732,7 @@ public boolean execute(String sql) throws SQLServerException { } checkClosed(); executeStatement(new StmtExecCmd(this, sql, EXECUTE, NO_GENERATED_KEYS)); - loggerExternal.exiting(getClassNameLogging(), "execute", Boolean.valueOf(null != resultSet)); + loggerExternal.exiting(getClassNameLogging(), "execute", null != resultSet); return null != resultSet; } @@ -1032,16 +1032,16 @@ final void resetForReexecute() throws SQLServerException { /* L0 */ public final int getMaxFieldSize() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getMaxFieldSize"); checkClosed(); - loggerExternal.exiting(getClassNameLogging(), "getMaxFieldSize", new Integer(maxFieldSize)); + loggerExternal.exiting(getClassNameLogging(), "getMaxFieldSize", maxFieldSize); return maxFieldSize; } /* L0 */ public final void setMaxFieldSize(int max) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "setMaxFieldSize", new Integer(max)); + loggerExternal.entering(getClassNameLogging(), "setMaxFieldSize", max); checkClosed(); if (max < 0) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidLength")); - Object[] msgArgs = {new Integer(max)}; + Object[] msgArgs = {max}; SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), null, true); } maxFieldSize = max; @@ -1051,7 +1051,7 @@ final void resetForReexecute() throws SQLServerException { /* L0 */ public final int getMaxRows() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getMaxRows"); checkClosed(); - loggerExternal.exiting(getClassNameLogging(), "getMaxRows", new Integer(maxRows)); + loggerExternal.exiting(getClassNameLogging(), "getMaxRows", maxRows); return maxRows; } @@ -1062,18 +1062,18 @@ public final long getLargeMaxRows() throws SQLServerException { // SQL Server only supports integer limits for setting max rows. // So, getLargeMaxRows() and getMaxRows() will return the same value. - loggerExternal.exiting(getClassNameLogging(), "getLargeMaxRows", new Long(maxRows)); + loggerExternal.exiting(getClassNameLogging(), "getLargeMaxRows", (long) maxRows); return (long) getMaxRows(); } /* L0 */ public final void setMaxRows(int max) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setMaxRows", new Integer(max)); + loggerExternal.entering(getClassNameLogging(), "setMaxRows", max); checkClosed(); if (max < 0) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidRowcount")); - Object[] msgArgs = {new Integer(max)}; + Object[] msgArgs = {max}; SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), null, true); } @@ -1089,7 +1089,7 @@ public final void setLargeMaxRows(long max) throws SQLServerException { DriverJDBCVersion.checkSupportsJDBC42(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setLargeMaxRows", new Long(max)); + loggerExternal.entering(getClassNameLogging(), "setLargeMaxRows", max); // SQL server only supports integer limits for setting max rows. // If is bigger than integer limits then throw an exception, otherwise call setMaxRows(int) @@ -1102,7 +1102,7 @@ public final void setLargeMaxRows(long max) throws SQLServerException { /* L0 */ public final void setEscapeProcessing(boolean enable) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setEscapeProcessing", Boolean.valueOf(enable)); + loggerExternal.entering(getClassNameLogging(), "setEscapeProcessing", enable); checkClosed(); escapeProcessing = enable; loggerExternal.exiting(getClassNameLogging(), "setEscapeProcessing"); @@ -1111,16 +1111,16 @@ public final void setLargeMaxRows(long max) throws SQLServerException { /* L0 */ public final int getQueryTimeout() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getQueryTimeout"); checkClosed(); - loggerExternal.exiting(getClassNameLogging(), "getQueryTimeout", new Integer(queryTimeout)); + loggerExternal.exiting(getClassNameLogging(), "getQueryTimeout", queryTimeout); return queryTimeout; } /* L0 */ public final void setQueryTimeout(int seconds) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "setQueryTimeout", new Integer(seconds)); + loggerExternal.entering(getClassNameLogging(), "setQueryTimeout", seconds); checkClosed(); if (seconds < 0) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidQueryTimeOutValue")); - Object[] msgArgs = {new Integer(seconds)}; + Object[] msgArgs = {seconds}; SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), null, true); } queryTimeout = seconds; @@ -1183,7 +1183,7 @@ public final java.sql.ResultSet getResultSet() throws SQLServerException { if (updateCount < Integer.MIN_VALUE || updateCount > Integer.MAX_VALUE) SQLServerException.makeFromDriverError(connection, this, SQLServerException.getErrString("R_updateCountOutofRange"), null, true); - loggerExternal.exiting(getClassNameLogging(), "getUpdateCount", new Long(updateCount)); + loggerExternal.exiting(getClassNameLogging(), "getUpdateCount", updateCount); return (int) updateCount; } @@ -1193,7 +1193,7 @@ public final long getLargeUpdateCount() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getUpdateCount"); checkClosed(); - loggerExternal.exiting(getClassNameLogging(), "getUpdateCount", new Long(updateCount)); + loggerExternal.exiting(getClassNameLogging(), "getUpdateCount", updateCount); return updateCount; } @@ -1268,7 +1268,7 @@ final void processResults() throws SQLServerException { // Don't just return the value from the getNextResult() call, however. // The getMoreResults method has a subtle spec for its return value (see above). getNextResult(); - loggerExternal.exiting(getClassNameLogging(), "getMoreResults", Boolean.valueOf(null != resultSet)); + loggerExternal.exiting(getClassNameLogging(), "getMoreResults", null != resultSet); return null != resultSet; } @@ -1609,14 +1609,14 @@ boolean consumeExecOutParam(TDSReader tdsReader) throws SQLServerException { public final void setFetchDirection(int nDir) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setFetchDirection", new Integer(nDir)); + loggerExternal.entering(getClassNameLogging(), "setFetchDirection", nDir); checkClosed(); if ((ResultSet.FETCH_FORWARD != nDir && ResultSet.FETCH_REVERSE != nDir && ResultSet.FETCH_UNKNOWN != nDir) || (ResultSet.FETCH_FORWARD != nDir && (SQLServerResultSet.TYPE_SS_DIRECT_FORWARD_ONLY == resultSetType || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == resultSetType))) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidFetchDirection")); - Object[] msgArgs = {new Integer(nDir)}; + Object[] msgArgs = {nDir}; SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), null, false); } @@ -1627,13 +1627,13 @@ public final void setFetchDirection(int nDir) throws SQLServerException { public final int getFetchDirection() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getFetchDirection"); checkClosed(); - loggerExternal.exiting(getClassNameLogging(), "getFetchDirection", new Integer(nFetchDirection)); + loggerExternal.exiting(getClassNameLogging(), "getFetchDirection", nFetchDirection); return nFetchDirection; } /* L0 */ public final void setFetchSize(int rows) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setFetchSize", new Integer(rows)); + loggerExternal.entering(getClassNameLogging(), "setFetchSize", rows); checkClosed(); if (rows < 0) SQLServerException.makeFromDriverError(connection, this, SQLServerException.getErrString("R_invalidFetchSize"), null, false); @@ -1645,21 +1645,21 @@ public final int getFetchDirection() throws SQLServerException { /* L0 */ public final int getFetchSize() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getFetchSize"); checkClosed(); - loggerExternal.exiting(getClassNameLogging(), "getFetchSize", new Integer(nFetchSize)); + loggerExternal.exiting(getClassNameLogging(), "getFetchSize", nFetchSize); return nFetchSize; } /* L0 */ public final int getResultSetConcurrency() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getResultSetConcurrency"); checkClosed(); - loggerExternal.exiting(getClassNameLogging(), "getResultSetConcurrency", new Integer(resultSetConcurrency)); + loggerExternal.exiting(getClassNameLogging(), "getResultSetConcurrency", resultSetConcurrency); return resultSetConcurrency; } /* L0 */ public final int getResultSetType() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getResultSetType"); checkClosed(); - loggerExternal.exiting(getClassNameLogging(), "getResultSetType", new Integer(appResultSetType)); + loggerExternal.exiting(getClassNameLogging(), "getResultSetType", appResultSetType); return appResultSetType; } @@ -1929,19 +1929,19 @@ private void doExecuteCursored(StmtExecCmd execCmd, tdsWriter.writeByte((byte) 0); // RPC procedure option 2 // OUT - tdsWriter.writeRPCInt(null, new Integer(0), true); + tdsWriter.writeRPCInt(null, 0, true); // IN tdsWriter.writeRPCStringUnicode(sql); // IN - tdsWriter.writeRPCInt(null, new Integer(getResultSetScrollOpt()), false); + tdsWriter.writeRPCInt(null, getResultSetScrollOpt(), false); // IN - tdsWriter.writeRPCInt(null, new Integer(getResultSetCCOpt()), false); + tdsWriter.writeRPCInt(null, getResultSetCCOpt(), false); // OUT - tdsWriter.writeRPCInt(null, new Integer(0), true); + tdsWriter.writeRPCInt(null, 0, true); ensureExecuteResultsReader(execCmd.startResponse(isResponseBufferingAdaptive)); startResults(); @@ -1954,14 +1954,14 @@ private void doExecuteCursored(StmtExecCmd execCmd, loggerExternal.entering(getClassNameLogging(), "getResultSetHoldability"); checkClosed(); int holdability = connection.getHoldability(); // For SQL Server must be the same as the connection - loggerExternal.exiting(getClassNameLogging(), "getResultSetHoldability", new Integer(holdability)); + loggerExternal.exiting(getClassNameLogging(), "getResultSetHoldability", holdability); return holdability; } public final boolean execute(java.lang.String sql, int autoGeneratedKeys) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) { - loggerExternal.entering(getClassNameLogging(), "execute", new Object[] {sql, new Integer(autoGeneratedKeys)}); + loggerExternal.entering(getClassNameLogging(), "execute", new Object[] {sql, autoGeneratedKeys}); if (Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); } @@ -1969,12 +1969,12 @@ public final boolean execute(java.lang.String sql, checkClosed(); if (autoGeneratedKeys != Statement.RETURN_GENERATED_KEYS && autoGeneratedKeys != Statement.NO_GENERATED_KEYS) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidAutoGeneratedKeys")); - Object[] msgArgs = {new Integer(autoGeneratedKeys)}; + Object[] msgArgs = {autoGeneratedKeys}; SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), null, false); } executeStatement(new StmtExecCmd(this, sql, EXECUTE, autoGeneratedKeys)); - loggerExternal.exiting(getClassNameLogging(), "execute", Boolean.valueOf(null != resultSet)); + loggerExternal.exiting(getClassNameLogging(), "execute", null != resultSet); return null != resultSet; } @@ -1987,7 +1987,7 @@ public final boolean execute(java.lang.String sql, SQLServerException.makeFromDriverError(connection, this, SQLServerException.getErrString("R_invalidColumnArrayLength"), null, false); } boolean fSuccess = execute(sql, Statement.RETURN_GENERATED_KEYS); - loggerExternal.exiting(getClassNameLogging(), "execute", Boolean.valueOf(fSuccess)); + loggerExternal.exiting(getClassNameLogging(), "execute", fSuccess); return fSuccess; } @@ -2000,14 +2000,14 @@ public final boolean execute(java.lang.String sql, SQLServerException.makeFromDriverError(connection, this, SQLServerException.getErrString("R_invalidColumnArrayLength"), null, false); } boolean fSuccess = execute(sql, Statement.RETURN_GENERATED_KEYS); - loggerExternal.exiting(getClassNameLogging(), "execute", Boolean.valueOf(fSuccess)); + loggerExternal.exiting(getClassNameLogging(), "execute", fSuccess); return fSuccess; } public final int executeUpdate(String sql, int autoGeneratedKeys) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) { - loggerExternal.entering(getClassNameLogging(), "executeUpdate", new Object[] {sql, new Integer(autoGeneratedKeys)}); + loggerExternal.entering(getClassNameLogging(), "executeUpdate", new Object[] {sql, autoGeneratedKeys}); if (Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); } @@ -2015,7 +2015,7 @@ public final int executeUpdate(String sql, checkClosed(); if (autoGeneratedKeys != Statement.RETURN_GENERATED_KEYS && autoGeneratedKeys != Statement.NO_GENERATED_KEYS) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidAutoGeneratedKeys")); - Object[] msgArgs = {new Integer(autoGeneratedKeys)}; + Object[] msgArgs = {autoGeneratedKeys}; SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), null, false); } executeStatement(new StmtExecCmd(this, sql, EXECUTE_UPDATE, autoGeneratedKeys)); @@ -2024,7 +2024,7 @@ public final int executeUpdate(String sql, if (updateCount < Integer.MIN_VALUE || updateCount > Integer.MAX_VALUE) SQLServerException.makeFromDriverError(connection, this, SQLServerException.getErrString("R_updateCountOutofRange"), null, true); - loggerExternal.exiting(getClassNameLogging(), "executeUpdate", new Long(updateCount)); + loggerExternal.exiting(getClassNameLogging(), "executeUpdate", updateCount); return (int) updateCount; } @@ -2034,7 +2034,7 @@ public final long executeLargeUpdate(String sql, DriverJDBCVersion.checkSupportsJDBC42(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) { - loggerExternal.entering(getClassNameLogging(), "executeLargeUpdate", new Object[] {sql, new Integer(autoGeneratedKeys)}); + loggerExternal.entering(getClassNameLogging(), "executeLargeUpdate", new Object[] {sql, autoGeneratedKeys}); if (Util.IsActivityTraceOn()) { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); } @@ -2042,11 +2042,11 @@ public final long executeLargeUpdate(String sql, checkClosed(); if (autoGeneratedKeys != Statement.RETURN_GENERATED_KEYS && autoGeneratedKeys != Statement.NO_GENERATED_KEYS) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidAutoGeneratedKeys")); - Object[] msgArgs = {new Integer(autoGeneratedKeys)}; + Object[] msgArgs = {autoGeneratedKeys}; SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), null, false); } executeStatement(new StmtExecCmd(this, sql, EXECUTE_UPDATE, autoGeneratedKeys)); - loggerExternal.exiting(getClassNameLogging(), "executeLargeUpdate", new Long(updateCount)); + loggerExternal.exiting(getClassNameLogging(), "executeLargeUpdate", updateCount); return updateCount; } @@ -2059,7 +2059,7 @@ public final int executeUpdate(java.lang.String sql, SQLServerException.makeFromDriverError(connection, this, SQLServerException.getErrString("R_invalidColumnArrayLength"), null, false); } int count = executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); - loggerExternal.exiting(getClassNameLogging(), "executeUpdate", new Integer(count)); + loggerExternal.exiting(getClassNameLogging(), "executeUpdate", count); return count; } @@ -2074,7 +2074,7 @@ public final long executeLargeUpdate(java.lang.String sql, SQLServerException.makeFromDriverError(connection, this, SQLServerException.getErrString("R_invalidColumnArrayLength"), null, false); } long count = executeLargeUpdate(sql, Statement.RETURN_GENERATED_KEYS); - loggerExternal.exiting(getClassNameLogging(), "executeLargeUpdate", new Long(count)); + loggerExternal.exiting(getClassNameLogging(), "executeLargeUpdate", count); return count; } @@ -2087,7 +2087,7 @@ public final int executeUpdate(java.lang.String sql, SQLServerException.makeFromDriverError(connection, this, SQLServerException.getErrString("R_invalidColumnArrayLength"), null, false); } int count = executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); - loggerExternal.exiting(getClassNameLogging(), "executeUpdate", new Integer(count)); + loggerExternal.exiting(getClassNameLogging(), "executeUpdate", count); return count; } @@ -2102,7 +2102,7 @@ public final long executeLargeUpdate(java.lang.String sql, SQLServerException.makeFromDriverError(connection, this, SQLServerException.getErrString("R_invalidColumnArrayLength"), null, false); } long count = executeLargeUpdate(sql, Statement.RETURN_GENERATED_KEYS); - loggerExternal.exiting(getClassNameLogging(), "executeLargeUpdate", new Long(count)); + loggerExternal.exiting(getClassNameLogging(), "executeLargeUpdate", count); return count; } @@ -2129,7 +2129,7 @@ public final ResultSet getGeneratedKeys() throws SQLServerException { /* L3 */ public final boolean getMoreResults(int mode) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "getMoreResults", new Integer(mode)); + loggerExternal.entering(getClassNameLogging(), "getMoreResults", mode); checkClosed(); if (KEEP_CURRENT_RESULT == mode) NotImplemented(); @@ -2148,7 +2148,7 @@ public final ResultSet getGeneratedKeys() throws SQLServerException { } } - loggerExternal.exiting(getClassNameLogging(), "getMoreResults", Boolean.valueOf(fResults)); + loggerExternal.exiting(getClassNameLogging(), "getMoreResults", fResults); return fResults; } @@ -2183,7 +2183,7 @@ public void setPoolable(boolean poolable) throws SQLException { public boolean isWrapperFor(Class iface) throws SQLException { loggerExternal.entering(getClassNameLogging(), "isWrapperFor"); boolean f = iface.isInstance(this); - loggerExternal.exiting(getClassNameLogging(), "isWrapperFor", Boolean.valueOf(f)); + loggerExternal.exiting(getClassNameLogging(), "isWrapperFor", f); return f; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SqlVariant.java b/src/main/java/com/microsoft/sqlserver/jdbc/SqlVariant.java new file mode 100644 index 0000000000..2d4c134361 --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SqlVariant.java @@ -0,0 +1,211 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ +package com.microsoft.sqlserver.jdbc; + +import java.text.MessageFormat; + +/** + * This class holds information regarding the basetype of a sql_variant data. + * + */ + +/** + * Enum for valid probBytes for different TDSTypes + * + */ +enum sqlVariantProbBytes { + INTN(0), + INT8(0), + INT4(0), + INT2(0), + INT1(0), + FLOAT4(0), + FLOAT8(0), + DATETIME4(0), + DATETIME8(0), + MONEY4(0), + MONEY8(0), + BITN(0), + GUID(0), + DATEN(0), + TIMEN(1), + DATETIME2N(1), + DECIMALN(2), + NUMERICN(2), + BIGBINARY(2), + BIGVARBINARY(2), + BIGCHAR(7), + BIGVARCHAR(7), + NCHAR(7), + NVARCHAR(7); + + private final int intValue; + + private static final int MAXELEMENTS = 23; + private static final sqlVariantProbBytes valuesTypes[] = new sqlVariantProbBytes[MAXELEMENTS]; + + private sqlVariantProbBytes(int intValue) { + this.intValue = intValue; + } + + int getIntValue() { + return intValue; + } + + static sqlVariantProbBytes valueOf(int intValue) { + sqlVariantProbBytes tdsType; + + if (!(0 <= intValue && intValue < valuesTypes.length) || null == (tdsType = valuesTypes[intValue])) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unknownSSType")); + Object[] msgArgs = {new Integer(intValue)}; + throw new IllegalArgumentException(form.format(msgArgs)); + } + + return tdsType; + } + +} + +public class SqlVariant { + + private int baseType; + private int precision; + private int scale; + private int maxLength; // for Character basetypes in sqlVariant + private SQLCollation collation; // for Character basetypes in sqlVariant + private boolean isBaseTypeTime = false; // we need this when we need to read time as timestamp (for instance in bulkcopy) + private JDBCType baseJDBCType; + + /** + * Constructor for sqlVariant + */ + SqlVariant(int baseType) { + this.baseType = baseType; + } + + /** + * Check if the basetype for variant is of time value + * + * @return + */ + boolean isBaseTypeTimeValue() { + return this.isBaseTypeTime; + } + + void setIsBaseTypeTimeValue(boolean isBaseTypeTime) { + this.isBaseTypeTime = isBaseTypeTime; + } + + /** + * store the base type for sql-variant + * + * @param baseType + */ + void setBaseType(int baseType) { + this.baseType = baseType; + } + + /** + * retrieves the base type for sql-variant + * + * @return + */ + int getBaseType() { + return this.baseType; + } + + /** + * Store the basetype as jdbc type + * + * @param baseJDBCType + */ + void setBaseJDBCType(JDBCType baseJDBCType) { + this.baseJDBCType = baseJDBCType; + } + + /** + * retrieves the base type as jdbc type + * + * @return + */ + JDBCType getBaseJDBCType() { + return this.baseJDBCType; + } + + /** + * stores the scale if applicable + * + * @param scale + */ + void setScale(int scale) { + this.scale = scale; + } + + /** + * retrieves the scale + * + * @return + */ + int getScale() { + return this.scale; + } + + /** + * stores the precision if applicable + * + * @param precision + */ + void setPrecision(int precision) { + this.precision = precision; + } + + /** + * retrieves the precision + * + * @return + */ + int getPrecision() { + return this.precision; + } + + /** + * stores the collation if applicable + * + * @param collation + */ + void setCollation(SQLCollation collation) { + this.collation = collation; + } + + /** + * Retrieves the collation + * + * @return + */ + SQLCollation getCollation() { + return this.collation; + } + + /** + * stores the maximum length + * + * @param maxLength + */ + void setMaxLength(int maxLength) { + this.maxLength = maxLength; + } + + /** + * retrieves the maximum length + * + * @return + */ + int getMaxLength() { + return this.maxLength; + } +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/TVP.java b/src/main/java/com/microsoft/sqlserver/jdbc/TVP.java index 7896215a3d..5113f1fc05 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/TVP.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/TVP.java @@ -47,7 +47,6 @@ class TVP { Map columnMetadata = null; Iterator> sourceDataTableRowIterator = null; ISQLServerDataRecord sourceRecord = null; - TVPType tvpType = null; // MultiPartIdentifierState diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java index 625a52797f..d630d6e48a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java @@ -244,7 +244,7 @@ static BigDecimal readBigDecimal(byte valueBytes[], String name = ""; String value = ""; StringBuilder builder; - + if (!tmpUrl.startsWith(sPrefix)) return null; @@ -390,7 +390,7 @@ else if (ch == ':') if (null != name) { if (logger.isLoggable(Level.FINE)) { if (false == name.equals(SQLServerDriverStringProperty.USER.toString())) { - if (!name.toLowerCase().contains("password")) { + if (!name.toLowerCase(Locale.ENGLISH).contains("password")) { logger.fine("Property:" + name + " Value:" + value); } else { @@ -605,7 +605,7 @@ static String readUnicodeString(byte[] b, catch (IndexOutOfBoundsException ex) { String txtMsg = SQLServerException.checkAndAppendClientConnId(SQLServerException.getErrString("R_stringReadError"), conn); MessageFormat form = new MessageFormat(txtMsg); - Object[] msgArgs = {new Integer(offset)}; + Object[] msgArgs = {offset}; // Re-throw SQLServerException if conversion fails. throw new SQLServerException(form.format(msgArgs), null, 0, ex); } @@ -714,6 +714,46 @@ static final byte[] asGuidByteArray(UUID aId) { return buffer; } + static final UUID readGUIDtoUUID(byte[] inputGUID) throws SQLServerException { + if (inputGUID.length != 16) { + throw new SQLServerException("guid length must be 16", null); + } + + // For the first three fields, UUID uses network byte order, + // Guid uses native byte order. So we need to reverse + // the first three fields before creating a UUID. + + byte tmpByte; + + // Reverse the first 4 bytes + tmpByte = inputGUID[0]; + inputGUID[0] = inputGUID[3]; + inputGUID[3] = tmpByte; + tmpByte = inputGUID[1]; + inputGUID[1] = inputGUID[2]; + inputGUID[2] = tmpByte; + + // Reverse the 5th and the 6th + tmpByte = inputGUID[4]; + inputGUID[4] = inputGUID[5]; + inputGUID[5] = tmpByte; + + // Reverse the 7th and the 8th + tmpByte = inputGUID[6]; + inputGUID[6] = inputGUID[7]; + inputGUID[7] = tmpByte; + + long msb = 0L; + for (int i = 0; i < 8; i++) { + msb = msb << 8 | ((long) inputGUID[i] & 0xFFL); + } + long lsb = 0L; + for (int i = 8; i < 16; i++) { + lsb = lsb << 8 | ((long) inputGUID[i] & 0xFFL); + } + return new UUID(msb, lsb); + } + static final String readGUID(byte[] inputGUID) throws SQLServerException { String guidTemplate = "NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN"; byte guid[] = inputGUID; @@ -911,7 +951,7 @@ else if (("" + value).contains("E")) { case TIME: case DATETIMEOFFSET: return ((null == scale) ? TDS.MAX_FRACTIONAL_SECONDS_SCALE : scale); - + case CLOB: return ((null == value) ? 0 : (DataTypes.NTEXT_MAX_CHARS * 2)); @@ -953,7 +993,7 @@ static synchronized boolean checkIfNeedNewAccessToken(SQLServerConnection connec } static final boolean use42Wrapper; - + static { boolean supportJDBC42 = true; try { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java index 113f580594..c836c031db 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java @@ -139,6 +139,9 @@ abstract void execute(DTV dtv, abstract void execute(DTV dtv, TVP tvpValue) throws SQLServerException; + + abstract void execute(DTV dtv, + SqlVariant sqlVariantValue) throws SQLServerException; } /** @@ -290,6 +293,10 @@ Object getSetterValue() { return impl.getSetterValue(); } + SqlVariant getInternalVariant() { + return impl.getInternalVariant(); + } + /** * Called by DTV implementation instances to change to a different DTV implementation. */ @@ -1070,7 +1077,7 @@ void execute(DTV dtv, * simply the assignment in the Java runtime). So the only way found is to convert the float to a string and init the double with that * string */ - Double doubleValue = (null == floatValue) ? null : new Double(floatValue.floatValue()); + Double doubleValue = (null == floatValue) ? null : (double) floatValue; tdsWriter.writeRPCDouble(name, doubleValue, isOutParam); } } @@ -1436,6 +1443,18 @@ void execute(DTV dtv, // Write the reader value as a stream of Unicode characters tdsWriter.writeRPCReaderUnicode(name, readerValue, dtv.getStreamSetterArgs().getLength(), isOutParam, collation); } + + /* + * (non-Javadoc) + * + * @see com.microsoft.sqlserver.jdbc.DTVExecuteOp#execute(com.microsoft.sqlserver.jdbc.DTV, microsoft.sql.SqlVariant) + */ + @Override + void execute(DTV dtv, + SqlVariant sqlVariantValue) throws SQLServerException { + tdsWriter.writeRPCSqlVariant(name, sqlVariantValue, isOutParam); + + } } /** @@ -1577,6 +1596,10 @@ final void executeOp(DTVExecuteOp op) throws SQLServerException { case STRUCT: unsupportedConversion = true; break; + + case SQL_VARIANT: + op.execute(this, (SqlVariant) null); + break; case UNKNOWN: default: @@ -1599,6 +1622,9 @@ final void executeOp(DTVExecuteOp op) throws SQLServerException { byte[] bArray = Util.asGuidByteArray((UUID) value); op.execute(this, bArray); } + else if (jdbcType.SQL_VARIANT == jdbcType) { + op.execute(this, String.valueOf(value)); + } else { if (null != cryptoMeta) { // if streaming types check for allowed data length in AE @@ -1956,6 +1982,8 @@ abstract void skipValue(TypeInfo typeInfo, boolean isDiscard) throws SQLServerException; abstract void initFromCompressedNull(); + + abstract SqlVariant getInternalVariant(); } /** @@ -1969,7 +1997,8 @@ final class AppDTVImpl extends DTVImpl { private Calendar cal; private Integer scale; private boolean forceEncrypt; - + private SqlVariant internalVariant; + final void skipValue(TypeInfo typeInfo, TDSReader tdsReader, boolean isDiscard) throws SQLServerException { @@ -2172,8 +2201,8 @@ void execute(DTV dtv, // Rescale the value if necessary if (null != bigDecimalValue) { Integer inScale = dtv.getScale(); - if (null != inScale && inScale.intValue() != bigDecimalValue.scale()) - bigDecimalValue = bigDecimalValue.setScale(inScale.intValue(), BigDecimal.ROUND_DOWN); + if (null != inScale && inScale != bigDecimalValue.scale()) + bigDecimalValue = bigDecimalValue.setScale(inScale, BigDecimal.ROUND_DOWN); } dtv.setValue(bigDecimalValue, JavaType.BIGDECIMAL); @@ -2261,7 +2290,7 @@ void execute(DTV dtv, // the actual stream length did not match then cancel the request. if (DataTypes.UNKNOWN_STREAM_LENGTH != readerLength && stringValue.length() != readerLength) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_mismatchedStreamLength")); - Object[] msgArgs = {Long.valueOf(readerLength), Integer.valueOf(stringValue.length())}; + Object[] msgArgs = {readerLength, stringValue.length()}; SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), "", true); } @@ -2281,6 +2310,17 @@ else if (null != collation execute(dtv, streamValue); } } + + /* + * (non-Javadoc) + * + * @see com.microsoft.sqlserver.jdbc.DTVExecuteOp#execute(com.microsoft.sqlserver.jdbc.DTV, microsoft.sql.SqlVariant) + */ + @Override + void execute(DTV dtv, + SqlVariant SqlVariantValue) throws SQLServerException { + } + } void setValue(DTV dtv, @@ -2371,6 +2411,26 @@ Object getValue(DTV dtv, Object getSetterValue() { return value; } + + /* + * (non-Javadoc) + * + * @see com.microsoft.sqlserver.jdbc.DTVImpl#getInternalVariant() + */ + @Override + SqlVariant getInternalVariant() { + return this.internalVariant; + } + + /** + * Sets the internal datatype of variant type + * + * @param type + * sql_variant internal type + */ + void setInternalVariant(SqlVariant type) { + this.internalVariant = type; + } } /** @@ -2398,10 +2458,18 @@ final class TypeInfo { SSType getSSType() { return ssType; } + + void setSSType(SSType ssType) { + this.ssType = ssType; + } SSLenType getSSLenType() { return ssLenType; } + + void setSSLenType(SSLenType ssLenType){ + this.ssLenType = ssLenType; + } String getSSTypeName() { return (SSType.UDT == ssType) ? udtTypeName : ssType.toString(); @@ -2410,14 +2478,25 @@ String getSSTypeName() { int getMaxLength() { return maxLength; } - + + void setMaxLength(int maxLength) { + this.maxLength = maxLength; + } int getPrecision() { return precision; } + + void setPrecision(int precision) { + this.precision = precision; + } int getDisplaySize() { return displaySize; } + + void setDisplaySize(int displaySize){ + this.displaySize = displaySize; + } int getScale() { return scale; @@ -2434,6 +2513,10 @@ void setSQLCollation(SQLCollation collation) { Charset getCharset() { return charset; } + + void setCharset(Charset charset){ + this.charset = charset; + } boolean isNullable() { return 0x0001 == (flags & 0x0001); @@ -2477,6 +2560,10 @@ short getFlagsAsShort() { void setFlags(Short flags) { this.flags = flags; } + + void setScale(int scale){ + this.scale = scale; + } //TypeInfo Builder enum defines a set of builders used to construct TypeInfo instances //for the various data types. Each builder builds a TypeInfo instance using a builder Strategy. @@ -3011,37 +3098,10 @@ public void apply(TypeInfo typeInfo, */ public void apply(TypeInfo typeInfo, TDSReader tdsReader) throws SQLServerException { - try { - SQLServerException.makeFromDriverError(tdsReader.getConnection(), null, SQLServerException.getErrString("R_variantNotSupported"), - null, false); - } - finally { - /* - * As the driver doesn't know how to process or skip the VARIANT type in TDS token stream, we send an interrupt Signal to server, - * and skips all the data received while waiting for the interrupt acknowledgment. - */ - int remainingPackets = 0; - - // Skip the current buffered packet - remainingPackets = tdsReader.availableCurrentPacket(); - tdsReader.skip(remainingPackets); - - // send interrupt to server - tdsReader.getCommand().interrupt(SQLServerException.getErrString("R_variantNotSupported")); - - /* - * Skip all data only if waiting for attention ack and until interrupt acknowledgment is received. - * - * Interrupt acknowledgment is a DONE token with the DONE_ATTN(0x0020) bit set. - */ - while (tdsReader.getCommand().attentionPending() && (TDS.TDS_DONE != tdsReader.peekTokenType()) - && (0 != (tdsReader.peekStatusFlag() & 0x0020))) { - remainingPackets = tdsReader.availableCurrentPacket(); - tdsReader.skip(remainingPackets); - } - tdsReader.getCommand().close(); + typeInfo.ssLenType = SSLenType.LONGLENTYPE; //Variant type should be LONGLENTYPE length. + typeInfo.maxLength = tdsReader.readInt(); + typeInfo.ssType = SSType.SQL_VARIANT; } - } }); private final TDSType tdsType; @@ -3312,7 +3372,7 @@ final class ServerDTVImpl extends DTVImpl { private int valueLength; private TDSReaderMark valueMark; private boolean isNull; - + private SqlVariant internalVariant; /** * Sets the value of the DTV to an app-specified Java type. * @@ -3494,9 +3554,11 @@ private void getValuePrep(TypeInfo typeInfo, valueLength = tdsReader.readInt(); } } - else { + + else if (SSType.SQL_VARIANT == typeInfo.getSSType()) { valueLength = tdsReader.readInt(); isNull = (0 == valueLength); + typeInfo.setSSType(SSType.SQL_VARIANT); } break; } @@ -3934,7 +3996,26 @@ Object getValue(DTV dtv, case GUID: convertedValue = tdsReader.readGUID(valueLength, jdbcType, streamGetterArgs.streamType); break; + + case SQL_VARIANT: + /** + * SQL_Variant has the following structure: + * 1- basetype: the underlying type + * 2- probByte: holds count of property bytes expected for a sql_variant structure + * 3- properties: For example VARCHAR type has 5 byte collation and 2 byte max length + * 4- dataValue: the data value + */ + int baseType = tdsReader.readUnsignedByte(); + int cbPropsActual = tdsReader.readUnsignedByte(); + // don't create new one, if we have already created an internalVariant object. For example, in bulkcopy + // when we are reading time column, we update the same internalvarianttype's JDBC to be timestamp + if (null == internalVariant) { + internalVariant = new SqlVariant(baseType); + } + convertedValue = readSqlVariant(baseType, cbPropsActual, valueLength, tdsReader, baseSSType, typeInfo, jdbcType, streamGetterArgs, + cal); + break; // Unknown SSType should have already been rejected by TypeInfo.setFromTDS() default: assert false : "Unexpected SSType " + typeInfo.getSSType(); @@ -3946,6 +4027,270 @@ Object getValue(DTV dtv, assert isNull || null != convertedValue; return convertedValue; } + + SqlVariant getInternalVariant() { + return internalVariant; + } + + /** + * Read the value inside sqlVariant. The reading differs based on what the internal baseType is. + * + * @return sql_variant value + * @since 6.3.0 + * @throws SQLServerException + */ + private Object readSqlVariant(int intbaseType, + int cbPropsActual, + int valueLength, + TDSReader tdsReader, + SSType baseSSType, + TypeInfo typeInfo, + JDBCType jdbcType, + InputStreamGetterArgs streamGetterArgs, + Calendar cal) throws SQLServerException { + Object convertedValue = null; + int lengthConsumed = 2 + cbPropsActual; // We have already read 2bytes for baseType earlier. + int expectedValueLength = valueLength - lengthConsumed; + SQLCollation collation = null; + int precision; + int scale; + int maxLength; + TDSType baseType = TDSType.valueOf(intbaseType); + switch (baseType) { + case INT8: + jdbcType = JDBCType.BIGINT; + convertedValue = DDC.convertLongToObject(tdsReader.readLong(), jdbcType, baseSSType, streamGetterArgs.streamType); + break; + + case INT4: + jdbcType = JDBCType.INTEGER; + convertedValue = DDC.convertIntegerToObject(tdsReader.readInt(), valueLength, jdbcType, streamGetterArgs.streamType); + break; + + case INT2: + jdbcType = JDBCType.SMALLINT; + convertedValue = DDC.convertIntegerToObject(tdsReader.readShort(), valueLength, jdbcType, streamGetterArgs.streamType); + break; + + case INT1: + jdbcType = JDBCType.TINYINT; + convertedValue = DDC.convertIntegerToObject(tdsReader.readUnsignedByte(), valueLength, jdbcType, streamGetterArgs.streamType); + break; + + case DECIMALN: + case NUMERICN: + if (TDSType.DECIMALN == baseType) + jdbcType = JDBCType.DECIMAL; + else if (TDSType.NUMERICN == baseType) + jdbcType = JDBCType.NUMERIC; + if (cbPropsActual != sqlVariantProbBytes.DECIMALN.getIntValue()) { // Numeric and decimal have the same probbytes value + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidProbbytes")); + throw new SQLServerException(form.format(new Object[] {baseType}), null, 0, null); + } + jdbcType = JDBCType.DECIMAL; + precision = tdsReader.readUnsignedByte(); + scale = tdsReader.readUnsignedByte(); + typeInfo.setScale(scale); // typeInfo needs to be updated. typeInfo is usually set when reading columnMetaData, but for sql_variant + // type the actual columnMetaData is is set when reading the data rows. + internalVariant.setPrecision(precision); + internalVariant.setScale(scale); + convertedValue = tdsReader.readDecimal(expectedValueLength, typeInfo, jdbcType, streamGetterArgs.streamType); + break; + + case FLOAT4: + jdbcType = JDBCType.REAL; + convertedValue = tdsReader.readReal(expectedValueLength, jdbcType, streamGetterArgs.streamType); + break; + + case FLOAT8: + jdbcType = JDBCType.FLOAT; + convertedValue = tdsReader.readFloat(expectedValueLength, jdbcType, streamGetterArgs.streamType); + break; + + case MONEY4: + jdbcType = JDBCType.SMALLMONEY; + typeInfo.setMaxLength(4); + precision = Long.toString(Long.MAX_VALUE).length(); + typeInfo.setPrecision(precision); + scale = 4; + typeInfo.setDisplaySize(("-" + "." + Integer.toString(Integer.MAX_VALUE)).length()); + typeInfo.setScale(scale); + internalVariant.setPrecision(precision); + internalVariant.setScale(scale); + convertedValue = tdsReader.readMoney(expectedValueLength, jdbcType, streamGetterArgs.streamType); + break; + + case MONEY8: + jdbcType = JDBCType.MONEY; + typeInfo.setMaxLength(8); + precision = Long.toString(Long.MAX_VALUE).length(); + scale = 4; + typeInfo.setPrecision(precision); + typeInfo.setDisplaySize(("-" + "." + Integer.toString(Integer.MAX_VALUE)).length()); + typeInfo.setScale(scale); + internalVariant.setPrecision(precision); + internalVariant.setScale(scale); + convertedValue = tdsReader.readMoney(expectedValueLength, jdbcType, streamGetterArgs.streamType); + break; + + case BIT1: + case BITN: + jdbcType = JDBCType.BIT; + switch (expectedValueLength) { + case 8: + convertedValue = DDC.convertLongToObject(tdsReader.readLong(), jdbcType, baseSSType, streamGetterArgs.streamType); + break; + + case 4: + convertedValue = DDC.convertIntegerToObject(tdsReader.readInt(), expectedValueLength, jdbcType, streamGetterArgs.streamType); + break; + + case 2: + convertedValue = DDC.convertIntegerToObject(tdsReader.readShort(), expectedValueLength, jdbcType, + streamGetterArgs.streamType); + break; + + case 1: + convertedValue = DDC.convertIntegerToObject(tdsReader.readUnsignedByte(), expectedValueLength, jdbcType, + streamGetterArgs.streamType); + break; + + default: + assert false : "Unexpected valueLength" + expectedValueLength; + break; + } + break; + + case BIGVARCHAR: + case BIGCHAR: + if (cbPropsActual != sqlVariantProbBytes.BIGCHAR.getIntValue()) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidProbbytes")); + throw new SQLServerException(form.format(new Object[] {baseType}), null, 0, null); + } + if (TDSType.BIGVARCHAR == baseType) + jdbcType = JDBCType.VARCHAR; + else if (TDSType.BIGCHAR == baseType) + jdbcType = JDBCType.CHAR; + collation = tdsReader.readCollation(); + typeInfo.setSQLCollation(collation); + typeInfo.setSSLenType(SSLenType.USHORTLENTYPE); + maxLength = tdsReader.readUnsignedShort(); + typeInfo.setMaxLength(maxLength); + if (maxLength > DataTypes.SHORT_VARTYPE_MAX_BYTES) + tdsReader.throwInvalidTDS(); + typeInfo.setDisplaySize(maxLength); + typeInfo.setPrecision(maxLength); + internalVariant.setPrecision(maxLength); + internalVariant.setCollation(collation); + typeInfo.setCharset(collation.getCharset()); + convertedValue = DDC.convertStreamToObject(new SimpleInputStream(tdsReader, expectedValueLength, streamGetterArgs, this), typeInfo, + jdbcType, streamGetterArgs); + break; + + case NCHAR: + case NVARCHAR: + if (cbPropsActual != sqlVariantProbBytes.NCHAR.getIntValue()) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidProbbytes")); + throw new SQLServerException(form.format(new Object[] {baseType}), null, 0, null); + } + if (TDSType.NCHAR == baseType) + jdbcType = JDBCType.NCHAR; + else if (TDSType.NVARCHAR == baseType) + jdbcType = JDBCType.NVARCHAR; + collation = tdsReader.readCollation(); + typeInfo.setSQLCollation(collation); + typeInfo.setSSLenType(SSLenType.USHORTLENTYPE); + maxLength = tdsReader.readUnsignedShort(); + if (maxLength > DataTypes.SHORT_VARTYPE_MAX_BYTES || 0 != maxLength % 2) + tdsReader.throwInvalidTDS(); + typeInfo.setDisplaySize(maxLength / 2); + typeInfo.setPrecision(maxLength / 2); + internalVariant.setPrecision(maxLength / 2); + internalVariant.setCollation(collation); + typeInfo.setCharset(Encoding.UNICODE.charset()); + convertedValue = DDC.convertStreamToObject(new SimpleInputStream(tdsReader, expectedValueLength, streamGetterArgs, this), typeInfo, + jdbcType, streamGetterArgs); + break; + + case DATETIME8: + jdbcType = JDBCType.DATETIME; + convertedValue = tdsReader.readDateTime(expectedValueLength, cal, jdbcType, streamGetterArgs.streamType); + break; + + case DATETIME4: + jdbcType = JDBCType.SMALLDATETIME; + convertedValue = tdsReader.readDateTime(expectedValueLength, cal, jdbcType, streamGetterArgs.streamType); + break; + + case DATEN: + jdbcType = JDBCType.DATE; + convertedValue = tdsReader.readDate(expectedValueLength, cal, jdbcType); + break; + + case TIMEN: + if (cbPropsActual != sqlVariantProbBytes.TIMEN.getIntValue()) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidProbbytes")); + throw new SQLServerException(form.format(new Object[] {baseType}), null, 0, null); + } + jdbcType = JDBCType.CHAR; // The reason we use char is to return nanoseconds + if (internalVariant.isBaseTypeTimeValue()) { + jdbcType = JDBCType.TIMESTAMP; + } + scale = tdsReader.readUnsignedByte(); + typeInfo.setScale(scale); + internalVariant.setScale(scale); + convertedValue = tdsReader.readTime(expectedValueLength, typeInfo, cal, jdbcType); + break; + + case DATETIME2N: + if (cbPropsActual != sqlVariantProbBytes.DATETIME2N.getIntValue()) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidProbbytes")); + throw new SQLServerException(form.format(new Object[] {baseType}), null, 0, null); + } + jdbcType = JDBCType.TIMESTAMP; + scale = tdsReader.readUnsignedByte(); + typeInfo.setScale(scale); + internalVariant.setScale(scale); + convertedValue = tdsReader.readDateTime2(expectedValueLength, typeInfo, cal, jdbcType); + break; + + case BIGBINARY: // e.g binary20, binary 512, binary 8000 -> reads as bigbinary + case BIGVARBINARY: + if (cbPropsActual != sqlVariantProbBytes.BIGBINARY.getIntValue()) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidProbbytes")); + throw new SQLServerException(form.format(new Object[] {baseType}), null, 0, null); + } + if (TDSType.BIGBINARY == baseType) + jdbcType = JDBCType.BINARY;// LONGVARCHAR; + else if (TDSType.BIGVARBINARY == baseType) + jdbcType = JDBCType.VARBINARY; + maxLength = tdsReader.readUnsignedShort(); + internalVariant.setMaxLength(maxLength); + typeInfo.setMaxLength(maxLength); + if (maxLength > DataTypes.SHORT_VARTYPE_MAX_BYTES) + tdsReader.throwInvalidTDS(); + typeInfo.setDisplaySize(2 * maxLength); + typeInfo.setPrecision(maxLength); + convertedValue = DDC.convertStreamToObject(new SimpleInputStream(tdsReader, expectedValueLength, streamGetterArgs, this), typeInfo, + jdbcType, streamGetterArgs); + break; + + case GUID: + jdbcType = JDBCType.GUID; + internalVariant.setBaseType(intbaseType); + internalVariant.setBaseJDBCType(jdbcType); + typeInfo.setDisplaySize("NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN".length()); + lengthConsumed = 2 + cbPropsActual; + convertedValue = tdsReader.readGUID(expectedValueLength, jdbcType, streamGetterArgs.streamType); + break; + + // Unknown SSType should have already been rejected by TypeInfo.setFromTDS() + default: + assert false : "Unexpected TDSType in Sql-Variant " + baseType; + break; + } + return convertedValue; + } Object getSetterValue() { // This function is never called, but must be implemented; it's abstract in DTVImpl. diff --git a/src/main/java/microsoft/sql/Types.java b/src/main/java/microsoft/sql/Types.java index 81d94801c1..36e820aade 100644 --- a/src/main/java/microsoft/sql/Types.java +++ b/src/main/java/microsoft/sql/Types.java @@ -52,4 +52,9 @@ private Types() { * The constant in the Java programming language, sometimes referred to as a type code, that identifies the Microsoft SQL type GUID. */ public static final int GUID = -145; + + /** + * The constant in the Java programming language, sometimes referred to as a type code, that identifies the Microsoft SQL type SQL_VARIANT. + */ + public static final int SQL_VARIANT = -156; } 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 b646d8e450..b164f004dd 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java @@ -13,11 +13,11 @@ import java.io.IOException; import java.sql.DriverManager; import java.sql.SQLException; -import java.sql.Statement; import java.util.Properties; import javax.xml.bind.DatatypeConverter; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -27,71 +27,84 @@ import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionKeyStoreProvider; import com.microsoft.sqlserver.jdbc.SQLServerConnection; import com.microsoft.sqlserver.jdbc.SQLServerException; +import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.jdbc.SQLServerStatementColumnEncryptionSetting; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBConnection; import com.microsoft.sqlserver.testframework.Utils; +import com.microsoft.sqlserver.testframework.util.Util; + import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; /** - * Setup for Always Encrypted test - * This test will work on Appveyor and Travis-ci as java key store gets created from the .yml scripts. Users on their local machine should create the - * keystore manually and save the alias name in JavaKeyStore.txt file. For local test purposes, put this in the target/test-classes directory + * Setup for Always Encrypted test This test will work on Appveyor and Travis-ci as java key store gets created from the .yml scripts. Users on their + * local machine should create the keystore manually and save the alias name in JavaKeyStore.txt file. For local test purposes, put this in the + * target/test-classes directory * */ @RunWith(JUnitPlatform.class) public class AESetup extends AbstractTest { - static String javaKeyStoreInputFile = "JavaKeyStore.txt"; - static String keyStoreName = "MSSQL_JAVA_KEYSTORE"; - static String jksName = "clientcert.jks"; + static final String javaKeyStoreInputFile = "JavaKeyStore.txt"; + static final String keyStoreName = "MSSQL_JAVA_KEYSTORE"; + static final String jksName = "clientcert.jks"; + static final String cmkName = "JDBC_CMK"; + static final String cekName = "JDBC_CEK"; + static final String secretstrJks = "password"; + static final String numericTable = "JDBCEncrpytedNumericTable"; + static final String charTable = "JDBCEncrpytedCharTable"; + static final String binaryTable = "JDBCEncrpytedBinaryTable"; + static final String dateTable = "JDBCEncrpytedDateTable"; + static final String uid = "171fbe25-4331-4765-a838-b2e3eea3e7ea"; + static String filePath = null; static String thumbprint = null; static SQLServerConnection con = null; - static Statement stmt = null; - static String cmkName = "JDBC_CMK"; - static String cekName = "JDBC_CEK"; + static SQLServerStatement stmt = null; static String keyPath = null; static String javaKeyAliases = null; static String OS = System.getProperty("os.name").toLowerCase(); static SQLServerColumnEncryptionKeyStoreProvider storeProvider = null; - static String secretstrJks = "password"; - static String numericTable = "numericTable"; + static SQLServerStatementColumnEncryptionSetting stmtColEncSetting = null; /** * Create connection, statement and generate path of resource file - * @throws Exception - * @throws TestAbortedException + * + * @throws Exception + * @throws TestAbortedException */ @BeforeAll static void setUpConnection() throws TestAbortedException, Exception { assumeTrue(13 <= new DBConnection(connectionString).getServerVersion(), "Aborting test case as SQL Server version is not compatible with Always encrypted "); + String AETestConenctionString = connectionString + ";sendTimeAsDateTime=false"; + readFromFile(javaKeyStoreInputFile, "Alias name"); - con = (SQLServerConnection) DriverManager.getConnection(connectionString); - stmt = con.createStatement(); - Utils.dropTableIfExists(numericTable, stmt); + con = (SQLServerConnection) DriverManager.getConnection(AETestConenctionString); + stmt = (SQLServerStatement) con.createStatement(); dropCEK(); dropCMK(); con.close(); - keyPath = Utils.getCurrentClassPath() + jksName; storeProvider = new SQLServerColumnEncryptionJavaKeyStoreProvider(keyPath, secretstrJks.toCharArray()); + stmtColEncSetting = SQLServerStatementColumnEncryptionSetting.Enabled; Properties info = new Properties(); info.setProperty("ColumnEncryptionSetting", "Enabled"); info.setProperty("keyStoreAuthentication", "JavaKeyStorePassword"); info.setProperty("keyStoreLocation", keyPath); info.setProperty("keyStoreSecret", secretstrJks); - con = (SQLServerConnection) DriverManager.getConnection(connectionString, info); - stmt = con.createStatement(); + con = (SQLServerConnection) DriverManager.getConnection(AETestConenctionString, info); + stmt = (SQLServerStatement) con.createStatement(); createCMK(keyStoreName, javaKeyAliases); + createCEK(storeProvider); } /** - * Read the alias from file which is created during creating jks - * If the jks and alias name in JavaKeyStore.txt does not exists, will not run! + * Read the alias from file which is created during creating jks If the jks and alias name in JavaKeyStore.txt does not exists, will not run! + * * @param inputFile * @param lookupValue * @throws IOException @@ -109,7 +122,7 @@ private static void readFromFile(String inputFile, while ((readLine = buffer.readLine()) != null) { if (readLine.trim().contains(lookupValue)) { - linecontents = readLine.split(" "); + linecontents = readLine.split(" "); javaKeyAliases = linecontents[2]; break; } @@ -117,10 +130,10 @@ private static void readFromFile(String inputFile, } catch (IOException e) { - fail(e.toString());; + fail(e.toString()); } - finally{ - if (null != buffer){ + finally { + if (null != buffer) { buffer.close(); } } @@ -128,17 +141,265 @@ private static void readFromFile(String inputFile, } /** - * Creating numeric table + * Create encrypted table for Binary + * + * @throws SQLException + */ + static void createBinaryTable() throws SQLException { + String sql = "create table " + binaryTable + " (" + "PlainBinary binary(20) null," + + "RandomizedBinary binary(20) ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicBinary binary(20) ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainVarbinary varbinary(50) null," + + "RandomizedVarbinary varbinary(50) ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicVarbinary varbinary(50) ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainVarbinaryMax varbinary(max) null," + + "RandomizedVarbinaryMax varbinary(max) ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicVarbinaryMax varbinary(max) ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainBinary512 binary(512) null," + + "RandomizedBinary512 binary(512) ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicBinary512 binary(512) ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainBinary8000 varbinary(8000) null," + + "RandomizedBinary8000 varbinary(8000) ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicBinary8000 varbinary(8000) ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + ");"; + + stmt.execute(sql); + } + + /** + * Create encrypted table for Char + * + * @throws SQLException + */ + static void createCharTable() throws SQLException { + String sql = "create table " + charTable + " (" + "PlainChar char(20) null," + + "RandomizedChar char(20) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicChar char(20) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainVarchar varchar(50) null," + + "RandomizedVarchar varchar(50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicVarchar varchar(50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainVarcharMax varchar(max) null," + + "RandomizedVarcharMax varchar(max) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicVarcharMax varchar(max) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainNchar nchar(30) null," + + "RandomizedNchar nchar(30) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicNchar nchar(30) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainNvarchar nvarchar(60) null," + + "RandomizedNvarchar nvarchar(60) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicNvarchar nvarchar(60) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainNvarcharMax nvarchar(max) null," + + "RandomizedNvarcharMax nvarchar(max) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicNvarcharMax nvarchar(max) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainUniqueidentifier uniqueidentifier null," + + "RandomizedUniqueidentifier uniqueidentifier ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicUniqueidentifier uniqueidentifier ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainVarchar8000 varchar(8000) null," + + "RandomizedVarchar8000 varchar(8000) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicVarchar8000 varchar(8000) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainNvarchar4000 nvarchar(4000) null," + + "RandomizedNvarchar4000 nvarchar(4000) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicNvarchar4000 nvarchar(4000) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + ");"; + + stmt.execute(sql); + } + + /** + * Create encrypted table for Date + * + * @throws SQLException + */ + static void createDateTable() throws SQLException { + String sql = "create table " + dateTable + " (" + "PlainDate date null," + + "RandomizedDate date ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicDate date ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainDatetime2Default datetime2 null," + + "RandomizedDatetime2Default datetime2 ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicDatetime2Default datetime2 ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainDatetimeoffsetDefault datetimeoffset null," + + "RandomizedDatetimeoffsetDefault datetimeoffset ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicDatetimeoffsetDefault datetimeoffset ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainTimeDefault time null," + + "RandomizedTimeDefault time ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicTimeDefault time ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainDatetime datetime null," + + "RandomizedDatetime datetime ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicDatetime datetime ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainSmalldatetime smalldatetime null," + + "RandomizedSmalldatetime smalldatetime ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicSmalldatetime smalldatetime ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + ");"; + + stmt.execute(sql); + } + + /** + * Create encrypted table for Numeric + * + * @throws SQLException */ - static void createNumericTable() { - String sql = "create table " + numericTable + " (" + "PlainSmallint smallint null," + static void createNumericTable() throws SQLException { + String sql = "create table " + numericTable + " (" + "PlainBit bit null," + + "RandomizedBit bit ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicBit bit ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainTinyint tinyint null," + + "RandomizedTinyint tinyint ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicTinyint tinyint ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainSmallint smallint null," + "RandomizedSmallint smallint ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + cekName + ") NULL," + "DeterministicSmallint smallint ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " - + cekName + ") NULL" + ");"; + + cekName + ") NULL," + + + "PlainInt int null," + + "RandomizedInt int ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicInt int ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainBigint bigint null," + + "RandomizedBigint bigint ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicBigint bigint ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainFloatDefault float null," + + "RandomizedFloatDefault float ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicFloatDefault float ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainFloat float(30) null," + + "RandomizedFloat float(30) ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicFloat float(30) ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainReal real null," + + "RandomizedReal real ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicReal real ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainDecimalDefault decimal null," + + "RandomizedDecimalDefault decimal ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicDecimalDefault decimal ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainDecimal decimal(10,5) null," + + "RandomizedDecimal decimal(10,5) ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicDecimal decimal(10,5) ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainNumericDefault numeric null," + + "RandomizedNumericDefault numeric ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicNumericDefault numeric ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainNumeric numeric(8,2) null," + + "RandomizedNumeric numeric(8,2) ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicNumeric numeric(8,2) ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainSmallMoney smallmoney null," + + "RandomizedSmallMoney smallmoney ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicSmallMoney smallmoney ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainMoney money null," + + "RandomizedMoney money ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicMoney money ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainDecimal2 decimal(28,4) null," + + "RandomizedDecimal2 decimal(28,4) ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicDecimal2 decimal(28,4) ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + "PlainNumeric2 numeric(28,4) null," + + "RandomizedNumeric2 numeric(28,4) ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + "DeterministicNumeric2 numeric(28,4) ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = " + + cekName + ") NULL," + + + ");"; try { stmt.execute(sql); + stmt.execute("DBCC FREEPROCCACHE"); } catch (SQLException e) { fail(e.toString()); @@ -147,6 +408,7 @@ static void createNumericTable() { /** * Create column master key + * * @param keyStoreName * @param keyPath * @throws SQLException @@ -160,6 +422,7 @@ private static void createCMK(String keyStoreName, /** * Create column encryption key + * * @param storeProvider * @param certStore * @throws SQLException @@ -170,12 +433,25 @@ static void createCEK(SQLServerColumnEncryptionKeyStoreProvider storeProvider) t 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" + DatatypeConverter.printHexBinary(key) + ")" + ";"; stmt.execute(cekSql); } + /** + * Drop all tables that are in use by AE + * + * @throws SQLException + */ + static void dropTables() throws SQLException { + Utils.dropTableIfExists(numericTable, stmt); + Utils.dropTableIfExists(charTable, stmt); + Utils.dropTableIfExists(binaryTable, stmt); + Utils.dropTableIfExists(dateTable, stmt); + } + /** * Dropping column encryption key + * * @throws SQLServerException * @throws SQLException */ @@ -187,6 +463,7 @@ static void dropCEK() throws SQLServerException, SQLException { /** * Dropping column master key + * * @throws SQLServerException * @throws SQLException */ diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/JDBCEncryptionDecryptionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/JDBCEncryptionDecryptionTest.java index ad3729d7af..2f647ef37e 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/JDBCEncryptionDecryptionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/JDBCEncryptionDecryptionTest.java @@ -7,14 +7,21 @@ */ package com.microsoft.sqlserver.jdbc.AlwaysEncrypted; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import java.math.BigDecimal; +import java.sql.Date; +import java.sql.JDBCType; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.LinkedList; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -22,9 +29,12 @@ import com.microsoft.sqlserver.jdbc.SQLServerException; import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; -import com.microsoft.sqlserver.jdbc.SQLServerStatementColumnEncryptionSetting; -import com.microsoft.sqlserver.testframework.DBConnection; -import com.microsoft.sqlserver.testframework.Utils; +import com.microsoft.sqlserver.jdbc.SQLServerResultSet; +import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.testframework.util.RandomData; +import com.microsoft.sqlserver.testframework.util.Util; + +import microsoft.sql.DateTimeOffset; /** * Tests Decryption and encryption of values @@ -32,30 +42,482 @@ */ @RunWith(JUnitPlatform.class) public class JDBCEncryptionDecryptionTest extends AESetup { - private static SQLServerPreparedStatement pstmt = null; - String[] values = {"10"}; + private SQLServerPreparedStatement pstmt = null; + + private boolean nullable = false; + private String[] numericValues = null; + private String[] numericValues2 = null; + private String[] numericValuesNull = null; + private String[] numericValuesNull2 = null; + private String[] charValues = null; + + private LinkedList byteValuesSetObject = null; + private LinkedList byteValuesNull = null; + + private LinkedList dateValues = null; /** - * Test encryption and decryption of numeric values + * Junit test case for char set string for string values * - * @throws Exception - * @throws TestAbortedException + * @throws SQLException */ @Test - @DisplayName("test numeric values") - public void testNumeric() throws TestAbortedException, Exception { - assumeTrue(13 <= new DBConnection(connectionString).getServerVersion(), - "Aborting test case as SQL Server version is not compatible with Always encrypted "); + public void testCharSpecificSetter() throws SQLException { + charValues = createCharValues(); + dropTables(); + createCharTable(); + populateCharNormalCase(charValues); + testChar(stmt, charValues); + testChar(null, charValues); + } - try { - createCEK(storeProvider); - createNumericTable(); - populateNumeric(values); - verifyResults(); - } - finally { - Utils.dropTableIfExists(numericTable, stmt); - } + /** + * Junit test case for char set object for string values + * + * @throws SQLException + */ + @Test + public void testCharSetObject() throws SQLException { + charValues = createCharValues(); + dropTables(); + createCharTable(); + populateCharSetObject(charValues); + testChar(stmt, charValues); + testChar(null, charValues); + } + + /** + * Junit test case for char set object for jdbc string values + * + * @throws SQLException + */ + @Test + public void testCharSetObjectWithJDBCTypes() throws SQLException { + skipTestForJava7(); + + charValues = createCharValues(); + dropTables(); + createCharTable(); + populateCharSetObjectWithJDBCTypes(charValues); + testChar(stmt, charValues); + testChar(null, charValues); + } + + /** + * Junit test case for char set string for null values + * + * @throws SQLException + */ + @Test + public void testCharSpecificSetterNull() throws SQLException { + String[] charValuesNull = {null, null, null, null, null, null, null, null, null}; + dropTables(); + createCharTable(); + populateCharNormalCase(charValuesNull); + testChar(stmt, charValuesNull); + testChar(null, charValuesNull); + } + + /** + * Junit test case for char set object for null values + * + * @throws SQLException + */ + @Test + public void testCharSetObjectNull() throws SQLException { + String[] charValuesNull = {null, null, null, null, null, null, null, null, null}; + dropTables(); + createCharTable(); + populateCharSetObject(charValuesNull); + testChar(stmt, charValuesNull); + testChar(null, charValuesNull); + } + + /** + * Junit test case for char set null for null values + * + * @throws SQLException + */ + @Test + public void testCharSetNull() throws SQLException { + String[] charValuesNull = {null, null, null, null, null, null, null, null, null}; + dropTables(); + createCharTable(); + populateCharNullCase(); + testChar(stmt, charValuesNull); + testChar(null, charValuesNull); + } + + /** + * Junit test case for binary set binary for binary values + * + * @throws SQLException + */ + @Test + public void testBinarySpecificSetter() throws SQLException { + LinkedList byteValues = createbinaryValues(false); + dropTables(); + createBinaryTable(); + populateBinaryNormalCase(byteValues); + testBinary(stmt, byteValues); + testBinary(null, byteValues); + } + + /** + * Junit test case for binary set object for binary values + * + * @throws SQLException + */ + @Test + public void testBinarySetobject() throws SQLException { + byteValuesSetObject = createbinaryValues(false); + dropTables(); + createBinaryTable(); + populateBinarySetObject(byteValuesSetObject); + testBinary(stmt, byteValuesSetObject); + testBinary(null, byteValuesSetObject); + } + + /** + * Junit test case for binary set null for binary values + * + * @throws SQLException + */ + @Test + public void testBinarySetNull() throws SQLException { + byteValuesNull = createbinaryValues(true); + dropTables(); + createBinaryTable(); + populateBinaryNullCase(); + testBinary(stmt, byteValuesNull); + testBinary(null, byteValuesNull); + } + + /** + * Junit test case for binary set binary for null values + * + * @throws SQLException + */ + @Test + public void testBinarySpecificSetterNull() throws SQLException { + byteValuesNull = createbinaryValues(true); + dropTables(); + createBinaryTable(); + populateBinaryNormalCase(null); + testBinary(stmt, byteValuesNull); + testBinary(null, byteValuesNull); + } + + /** + * Junit test case for binary set object for null values + * + * @throws SQLException + */ + @Test + public void testBinarysetObjectNull() throws SQLException { + byteValuesNull = createbinaryValues(true); + dropTables(); + createBinaryTable(); + populateBinarySetObject(null); + testBinary(stmt, byteValuesNull); + testBinary(null, byteValuesNull); + } + + /** + * Junit test case for binary set object for jdbc type binary values + * + * @throws SQLException + */ + @Test + public void testBinarySetObjectWithJDBCTypes() throws SQLException { + skipTestForJava7(); + + byteValuesSetObject = createbinaryValues(false); + dropTables(); + createBinaryTable(); + populateBinarySetObjectWithJDBCType(byteValuesSetObject); + testBinary(stmt, byteValuesSetObject); + testBinary(null, byteValuesSetObject); + } + + /** + * Junit test case for date set date for date values + * + * @throws SQLException + */ + @Test + public void testDateSpecificSetter() throws SQLException { + dateValues = createTemporalTypes(); + dropTables(); + createDateTable(); + populateDateNormalCase(dateValues); + testDate(stmt, dateValues); + testDate(null, dateValues); + } + + /** + * Junit test case for date set object for date values + * + * @throws SQLException + */ + @Test + public void testDateSetObject() throws SQLException { + dateValues = createTemporalTypes(); + dropTables(); + createDateTable(); + populateDateSetObject(dateValues, ""); + testDate(stmt, dateValues); + testDate(null, dateValues); + } + + /** + * Junit test case for date set object for java date values + * + * @throws SQLException + */ + @Test + public void testDateSetObjectWithJavaType() throws SQLException { + dateValues = createTemporalTypes(); + dropTables(); + createDateTable(); + populateDateSetObject(dateValues, "setwithJavaType"); + testDate(stmt, dateValues); + testDate(null, dateValues); + } + + /** + * Junit test case for date set object for jdbc date values + * + * @throws SQLException + */ + @Test + public void testDateSetObjectWithJDBCType() throws SQLException { + dateValues = createTemporalTypes(); + dropTables(); + createDateTable(); + populateDateSetObject(dateValues, "setwithJDBCType"); + testDate(stmt, dateValues); + testDate(null, dateValues); + } + + /** + * Junit test case for date set date for min/max date values + * + * @throws SQLException + */ + @Test + public void testDateSpecificSetterMinMaxValue() throws SQLException { + RandomData.returnMinMax = true; + dateValues = createTemporalTypes(); + dropTables(); + createDateTable(); + populateDateNormalCase(dateValues); + testDate(stmt, dateValues); + testDate(null, dateValues); + } + + /** + * Junit test case for date set date for null values + * + * @throws SQLException + */ + @Test + public void testDateSetNull() throws SQLException { + RandomData.returnNull = true; + nullable = true; + + dateValues = createTemporalTypes(); + dropTables(); + createDateTable(); + populateDateNullCase(); + testDate(stmt, dateValues); + testDate(null, dateValues); + + nullable = false; + RandomData.returnNull = false; + } + + /** + * Junit test case for date set object for null values + * + * @throws SQLException + */ + @Test + public void testDateSetObjectNull() throws SQLException { + RandomData.returnNull = true; + nullable = true; + + dateValues = createTemporalTypes(); + dropTables(); + createDateTable(); + populateDateSetObjectNull(); + testDate(stmt, dateValues); + testDate(null, dateValues); + + nullable = false; + RandomData.returnNull = false; + } + + /** + * Junit test case for numeric set numeric for numeric values + * + * @throws SQLException + */ + @Test + public void testNumericSpecificSetter() throws TestAbortedException, Exception { + numericValues = createNumericValues(); + numericValues2 = new String[numericValues.length]; + System.arraycopy(numericValues, 0, numericValues2, 0, numericValues.length); + + dropTables(); + createNumericTable(); + populateNumeric(numericValues); + testNumeric(stmt, numericValues, false); + testNumeric(null, numericValues2, false); + } + + /** + * Junit test case for numeric set object for numeric values + * + * @throws SQLException + */ + @Test + public void testNumericSetObject() throws SQLException { + numericValues = createNumericValues(); + numericValues2 = new String[numericValues.length]; + System.arraycopy(numericValues, 0, numericValues2, 0, numericValues.length); + + dropTables(); + createNumericTable(); + populateNumericSetObject(numericValues); + testNumeric(null, numericValues, false); + testNumeric(stmt, numericValues2, false); + } + + /** + * Junit test case for numeric set object for jdbc type numeric values + * + * @throws SQLException + */ + @Test + public void testNumericSetObjectWithJDBCTypes() throws SQLException { + skipTestForJava7(); + + numericValues = createNumericValues(); + numericValues2 = new String[numericValues.length]; + System.arraycopy(numericValues, 0, numericValues2, 0, numericValues.length); + + dropTables(); + createNumericTable(); + populateNumericSetObjectWithJDBCTypes(numericValues); + testNumeric(stmt, numericValues, false); + testNumeric(null, numericValues2, false); + } + + /** + * Junit test case for numeric set numeric for max numeric values + * + * @throws SQLException + */ + @Test + public void testNumericSpecificSetterMaxValue() throws SQLException { + String[] numericValuesBoundaryPositive = {"true", "255", "32767", "2147483647", "9223372036854775807", "1.79E308", "1.123", "3.4E38", + "999999999999999999", "12345.12345", "999999999999999999", "567812.78", "214748.3647", "922337203685477.5807", + "999999999999999999999999.9999", "999999999999999999999999.9999"}; + String[] numericValuesBoundaryPositive2 = {"true", "255", "32767", "2147483647", "9223372036854775807", "1.79E308", "1.123", "3.4E38", + "999999999999999999", "12345.12345", "999999999999999999", "567812.78", "214748.3647", "922337203685477.5807", + "999999999999999999999999.9999", "999999999999999999999999.9999"}; + + dropTables(); + createNumericTable(); + populateNumeric(numericValuesBoundaryPositive); + testNumeric(stmt, numericValuesBoundaryPositive, false); + testNumeric(null, numericValuesBoundaryPositive2, false); + } + + /** + * Junit test case for numeric set numeric for min numeric values + * + * @throws SQLException + */ + @Test + public void testNumericSpecificSetterMinValue() throws SQLException { + String[] numericValuesBoundaryNegtive = {"false", "0", "-32768", "-2147483648", "-9223372036854775808", "-1.79E308", "1.123", "-3.4E38", + "999999999999999999", "12345.12345", "999999999999999999", "567812.78", "-214748.3648", "-922337203685477.5808", + "999999999999999999999999.9999", "999999999999999999999999.9999"}; + String[] numericValuesBoundaryNegtive2 = {"false", "0", "-32768", "-2147483648", "-9223372036854775808", "-1.79E308", "1.123", "-3.4E38", + "999999999999999999", "12345.12345", "999999999999999999", "567812.78", "-214748.3648", "-922337203685477.5808", + "999999999999999999999999.9999", "999999999999999999999999.9999"}; + + dropTables(); + createNumericTable(); + populateNumeric(numericValuesBoundaryNegtive); + testNumeric(stmt, numericValuesBoundaryNegtive, false); + testNumeric(null, numericValuesBoundaryNegtive2, false); + } + + /** + * Junit test case for numeric set numeric for null values + * + * @throws SQLException + */ + @Test + public void testNumericSpecificSetterNull() throws SQLException { + nullable = true; + RandomData.returnNull = true; + numericValuesNull = createNumericValues(); + numericValuesNull2 = new String[numericValuesNull.length]; + System.arraycopy(numericValuesNull, 0, numericValuesNull2, 0, numericValuesNull.length); + + dropTables(); + createNumericTable(); + populateNumericNullCase(numericValuesNull); + testNumeric(stmt, numericValuesNull, true); + testNumeric(null, numericValuesNull2, true); + + nullable = false; + RandomData.returnNull = false; + } + + /** + * Junit test case for numeric set object for null values + * + * @throws SQLException + */ + @Test + public void testNumericSpecificSetterSetObjectNull() throws SQLException { + nullable = true; + RandomData.returnNull = true; + numericValuesNull = createNumericValues(); + numericValuesNull2 = new String[numericValuesNull.length]; + System.arraycopy(numericValuesNull, 0, numericValuesNull2, 0, numericValuesNull.length); + + dropTables(); + createNumericTable(); + populateNumericSetObjectNull(); + testNumeric(stmt, numericValuesNull, true); + testNumeric(null, numericValuesNull2, true); + + nullable = false; + RandomData.returnNull = false; + } + + /** + * Junit test case for numeric set numeric for null normalization values + * + * @throws SQLException + */ + @Test + public void testNumericNormalization() throws SQLException { + String[] numericValuesNormalization = {"true", "1", "127", "100", "100", "1.123", "1.123", "1.123", "123456789123456789", "12345.12345", + "987654321123456789", "567812.78", "7812.7812", "7812.7812", "999999999999999999999999.9999", "999999999999999999999999.9999"}; + String[] numericValuesNormalization2 = {"true", "1", "127", "100", "100", "1.123", "1.123", "1.123", "123456789123456789", "12345.12345", + "987654321123456789", "567812.78", "7812.7812", "7812.7812", "999999999999999999999999.9999", "999999999999999999999999.9999"}; + dropTables(); + createNumericTable(); + populateNumericNormalization(numericValuesNormalization); + testNumeric(stmt, numericValuesNormalization, false); + testNumeric(null, numericValuesNormalization2, false); } /** @@ -66,64 +528,1940 @@ public void testNumeric() throws TestAbortedException, Exception { */ @AfterAll static void dropAll() throws SQLServerException, SQLException { - Utils.dropTableIfExists(numericTable, stmt); + dropTables(); dropCEK(); dropCMK(); - if (null != stmt) { - stmt.close(); + Util.close(null, stmt, connection); + } + + private void populateBinaryNormalCase(LinkedList byteValues) throws SQLException { + String sql = "insert into " + binaryTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?" + ")"; + + pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + + // binary20 + for (int i = 1; i <= 3; i++) { + if (null == byteValues) { + pstmt.setBytes(i, null); + } + else { + pstmt.setBytes(i, byteValues.get(0)); + } + } + + // varbinary50 + for (int i = 4; i <= 6; i++) { + if (null == byteValues) { + pstmt.setBytes(i, null); + } + else { + pstmt.setBytes(i, byteValues.get(1)); + } + } + + // varbinary(max) + for (int i = 7; i <= 9; i++) { + if (null == byteValues) { + pstmt.setBytes(i, null); + } + else { + pstmt.setBytes(i, byteValues.get(2)); + } + } + + // binary(512) + for (int i = 10; i <= 12; i++) { + if (null == byteValues) { + pstmt.setBytes(i, null); + } + else { + pstmt.setBytes(i, byteValues.get(3)); + } } - if (null != con) { - con.close(); + + // varbinary(8000) + for (int i = 13; i <= 15; i++) { + if (null == byteValues) { + pstmt.setBytes(i, null); + } + else { + pstmt.setBytes(i, byteValues.get(4)); + } } + + pstmt.execute(); + Util.close(null, pstmt, null); } - /** - * Populating the table - * - * @param values - * @throws SQLException - */ - private void populateNumeric(String[] values) throws SQLException { - String sql = "insert into " + numericTable + " values( " + "?,?,?" + ")"; + private void populateBinarySetObject(LinkedList byteValues) throws SQLException { + String sql = "insert into " + binaryTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?" + ")"; - pstmt = (SQLServerPreparedStatement) con.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, - connection.getHoldability(), SQLServerStatementColumnEncryptionSetting.Enabled); + pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + // binary(20) for (int i = 1; i <= 3; i++) { - pstmt.setShort(i, Short.valueOf(values[0])); + if (null == byteValues) { + pstmt.setObject(i, null, java.sql.Types.BINARY); + } + else { + pstmt.setObject(i, byteValues.get(0)); + } } - pstmt.execute(); - if (null != pstmt) { - pstmt.close(); + + // varbinary(50) + for (int i = 4; i <= 6; i++) { + if (null == byteValues) { + pstmt.setObject(i, null, java.sql.Types.BINARY); + } + else { + pstmt.setObject(i, byteValues.get(1)); + } + } + + // varbinary(max) + for (int i = 7; i <= 9; i++) { + if (null == byteValues) { + pstmt.setObject(i, null, java.sql.Types.BINARY); + } + else { + pstmt.setObject(i, byteValues.get(2)); + } + } + + // binary(512) + for (int i = 10; i <= 12; i++) { + if (null == byteValues) { + pstmt.setObject(i, null, java.sql.Types.BINARY); + } + else { + pstmt.setObject(i, byteValues.get(3)); + } + } + + // varbinary(8000) + for (int i = 13; i <= 15; i++) { + if (null == byteValues) { + pstmt.setObject(i, null, java.sql.Types.BINARY); + } + else { + pstmt.setObject(i, byteValues.get(4)); + } } + + pstmt.execute(); + Util.close(null, pstmt, null); } - /** - * Verify the decryption and encryption of values - * - * @throws NumberFormatException - * @throws SQLException - */ - private void verifyResults() throws NumberFormatException, SQLException { - String sql = "select * from " + numericTable; - pstmt = (SQLServerPreparedStatement) connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, - connection.getHoldability(), SQLServerStatementColumnEncryptionSetting.Enabled); - ResultSet rs = null; + private void populateBinarySetObjectWithJDBCType(LinkedList byteValues) throws SQLException { + String sql = "insert into " + binaryTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?" + ")"; - rs = pstmt.executeQuery(); + pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); - while (rs.next()) { - assertEquals(Short.valueOf(values[0]), rs.getObject(1)); - assertEquals(Short.valueOf(values[0]), rs.getObject(2)); - assertEquals(Short.valueOf(values[0]), rs.getObject(3)); + // binary(20) + for (int i = 1; i <= 3; i++) { + if (null == byteValues) { + pstmt.setObject(i, null, JDBCType.BINARY); + } + else { + pstmt.setObject(i, byteValues.get(0), JDBCType.BINARY); + } + } + + // varbinary(50) + for (int i = 4; i <= 6; i++) { + if (null == byteValues) { + pstmt.setObject(i, null, JDBCType.VARBINARY); + } + else { + pstmt.setObject(i, byteValues.get(1), JDBCType.VARBINARY); + } + } + + // varbinary(max) + for (int i = 7; i <= 9; i++) { + if (null == byteValues) { + pstmt.setObject(i, null, JDBCType.VARBINARY); + } + else { + pstmt.setObject(i, byteValues.get(2), JDBCType.VARBINARY); + } } - if (null != rs) { - rs.close(); + // binary(512) + for (int i = 10; i <= 12; i++) { + if (null == byteValues) { + pstmt.setObject(i, null, JDBCType.BINARY); + } + else { + pstmt.setObject(i, byteValues.get(3), JDBCType.BINARY); + } } - if (null != pstmt) { - pstmt.close(); + + // varbinary(8000) + for (int i = 13; i <= 15; i++) { + if (null == byteValues) { + pstmt.setObject(i, null, JDBCType.VARBINARY); + } + else { + pstmt.setObject(i, byteValues.get(4), JDBCType.VARBINARY); + } } + + pstmt.execute(); + Util.close(null, pstmt, null); } + private void populateBinaryNullCase() throws SQLException { + String sql = "insert into " + binaryTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?" + ")"; + + pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + + // binary + for (int i = 1; i <= 3; i++) { + pstmt.setNull(i, java.sql.Types.BINARY); + } + + // varbinary, varbinary(max) + for (int i = 4; i <= 9; i++) { + pstmt.setNull(i, java.sql.Types.VARBINARY); + } + + // binary512 + for (int i = 10; i <= 12; i++) { + pstmt.setNull(i, java.sql.Types.BINARY); + } + + // varbinary(8000) + for (int i = 13; i <= 15; i++) { + pstmt.setNull(i, java.sql.Types.VARBINARY); + } + + pstmt.execute(); + Util.close(null, pstmt, null); + } + + private void populateCharNormalCase(String[] charValues) throws SQLException { + String sql = "insert into " + charTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + + "?,?,?" + ")"; + + pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + + // char + for (int i = 1; i <= 3; i++) { + pstmt.setString(i, charValues[0]); + } + + // varchar + for (int i = 4; i <= 6; i++) { + pstmt.setString(i, charValues[1]); + } + + // varchar(max) + for (int i = 7; i <= 9; i++) { + pstmt.setString(i, charValues[2]); + } + + // nchar + for (int i = 10; i <= 12; i++) { + pstmt.setNString(i, charValues[3]); + } + + // nvarchar + for (int i = 13; i <= 15; i++) { + pstmt.setNString(i, charValues[4]); + } + + // varchar(max) + for (int i = 16; i <= 18; i++) { + pstmt.setNString(i, charValues[5]); + } + + // uniqueidentifier + for (int i = 19; i <= 21; i++) { + if (null == charValues[6]) { + pstmt.setUniqueIdentifier(i, null); + } + else { + pstmt.setUniqueIdentifier(i, uid); + } + } + + // varchar8000 + for (int i = 22; i <= 24; i++) { + pstmt.setString(i, charValues[7]); + } + + // nvarchar4000 + for (int i = 25; i <= 27; i++) { + pstmt.setNString(i, charValues[8]); + } + + pstmt.execute(); + Util.close(null, pstmt, null); + } + + private void populateCharSetObject(String[] charValues) throws SQLException { + String sql = "insert into " + charTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + + "?,?,?" + ")"; + + pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + + // char + for (int i = 1; i <= 3; i++) { + pstmt.setObject(i, charValues[0]); + } + + // varchar + for (int i = 4; i <= 6; i++) { + pstmt.setObject(i, charValues[1]); + } + + // varchar(max) + for (int i = 7; i <= 9; i++) { + pstmt.setObject(i, charValues[2], java.sql.Types.LONGVARCHAR); + } + + // nchar + for (int i = 10; i <= 12; i++) { + pstmt.setObject(i, charValues[3], java.sql.Types.NCHAR); + } + + // nvarchar + for (int i = 13; i <= 15; i++) { + pstmt.setObject(i, charValues[4], java.sql.Types.NCHAR); + } + + // nvarchar(max) + for (int i = 16; i <= 18; i++) { + pstmt.setObject(i, charValues[5], java.sql.Types.LONGNVARCHAR); + } + + // uniqueidentifier + for (int i = 19; i <= 21; i++) { + pstmt.setObject(i, charValues[6], microsoft.sql.Types.GUID); + } + + // varchar8000 + for (int i = 22; i <= 24; i++) { + pstmt.setObject(i, charValues[7]); + } + + // nvarchar4000 + for (int i = 25; i <= 27; i++) { + pstmt.setObject(i, charValues[8], java.sql.Types.NCHAR); + } + + pstmt.execute(); + Util.close(null, pstmt, null); + } + + private void populateCharSetObjectWithJDBCTypes(String[] charValues) throws SQLException { + String sql = "insert into " + charTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + + "?,?,?" + ")"; + + pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + + // char + for (int i = 1; i <= 3; i++) { + pstmt.setObject(i, charValues[0], JDBCType.CHAR); + } + + // varchar + for (int i = 4; i <= 6; i++) { + pstmt.setObject(i, charValues[1], JDBCType.VARCHAR); + } + + // varchar(max) + for (int i = 7; i <= 9; i++) { + pstmt.setObject(i, charValues[2], JDBCType.LONGVARCHAR); + } + + // nchar + for (int i = 10; i <= 12; i++) { + pstmt.setObject(i, charValues[3], JDBCType.NCHAR); + } + + // nvarchar + for (int i = 13; i <= 15; i++) { + pstmt.setObject(i, charValues[4], JDBCType.NVARCHAR); + } + + // nvarchar(max) + for (int i = 16; i <= 18; i++) { + pstmt.setObject(i, charValues[5], JDBCType.LONGNVARCHAR); + } + + // uniqueidentifier + for (int i = 19; i <= 21; i++) { + pstmt.setObject(i, charValues[6], microsoft.sql.Types.GUID); + } + + // varchar8000 + for (int i = 22; i <= 24; i++) { + pstmt.setObject(i, charValues[7], JDBCType.VARCHAR); + } + + // vnarchar4000 + for (int i = 25; i <= 27; i++) { + pstmt.setObject(i, charValues[8], JDBCType.NVARCHAR); + } + + pstmt.execute(); + Util.close(null, pstmt, null); + } + + private void populateCharNullCase() throws SQLException { + String sql = "insert into " + charTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + + "?,?,?" + ")"; + + pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + + // char + for (int i = 1; i <= 3; i++) { + pstmt.setNull(i, java.sql.Types.CHAR); + } + + // varchar, varchar(max) + for (int i = 4; i <= 9; i++) { + pstmt.setNull(i, java.sql.Types.VARCHAR); + } + + // nchar + for (int i = 10; i <= 12; i++) { + pstmt.setNull(i, java.sql.Types.NCHAR); + } + + // nvarchar, varchar(max) + for (int i = 13; i <= 18; i++) { + pstmt.setNull(i, java.sql.Types.NVARCHAR); + } + + // uniqueidentifier + for (int i = 19; i <= 21; i++) { + pstmt.setNull(i, microsoft.sql.Types.GUID); + + } + + // varchar8000 + for (int i = 22; i <= 24; i++) { + pstmt.setNull(i, java.sql.Types.VARCHAR); + } + + // nvarchar4000 + for (int i = 25; i <= 27; i++) { + pstmt.setNull(i, java.sql.Types.NVARCHAR); + } + + pstmt.execute(); + Util.close(null, pstmt, null); + } + + private void populateDateNormalCase(LinkedList dateValues) throws SQLException { + String sql = "insert into " + dateTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?" + ")"; + + SQLServerPreparedStatement sqlPstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + + // date + for (int i = 1; i <= 3; i++) { + sqlPstmt.setDate(i, (Date) dateValues.get(0)); + } + + // datetime2 default + for (int i = 4; i <= 6; i++) { + sqlPstmt.setTimestamp(i, (Timestamp) dateValues.get(1)); + } + + // datetimeoffset default + for (int i = 7; i <= 9; i++) { + sqlPstmt.setDateTimeOffset(i, (DateTimeOffset) dateValues.get(2)); + } + + // time default + for (int i = 10; i <= 12; i++) { + sqlPstmt.setTime(i, (Time) dateValues.get(3)); + } + + // datetime + for (int i = 13; i <= 15; i++) { + sqlPstmt.setDateTime(i, (Timestamp) dateValues.get(4)); + } + + // smalldatetime + for (int i = 16; i <= 18; i++) { + sqlPstmt.setSmallDateTime(i, (Timestamp) dateValues.get(5)); + } + + sqlPstmt.execute(); + Util.close(null, sqlPstmt, null); + } + + private void populateDateSetObject(LinkedList dateValues, + String setter) throws SQLException { + if (setter.equalsIgnoreCase("setwithJDBCType")) { + skipTestForJava7(); + } + + String sql = "insert into " + dateTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?" + ")"; + + SQLServerPreparedStatement sqlPstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + + // date + for (int i = 1; i <= 3; i++) { + if (setter.equalsIgnoreCase("setwithJavaType")) + sqlPstmt.setObject(i, (Date) dateValues.get(0), java.sql.Types.DATE); + else if (setter.equalsIgnoreCase("setwithJDBCType")) + sqlPstmt.setObject(i, (Date) dateValues.get(0), JDBCType.DATE); + else + sqlPstmt.setObject(i, (Date) dateValues.get(0)); + } + + // datetime2 default + for (int i = 4; i <= 6; i++) { + if (setter.equalsIgnoreCase("setwithJavaType")) + sqlPstmt.setObject(i, (Timestamp) dateValues.get(1), java.sql.Types.TIMESTAMP); + else if (setter.equalsIgnoreCase("setwithJDBCType")) + sqlPstmt.setObject(i, (Timestamp) dateValues.get(1), JDBCType.TIMESTAMP); + else + sqlPstmt.setObject(i, (Timestamp) dateValues.get(1)); + } + + // datetimeoffset default + for (int i = 7; i <= 9; i++) { + if (setter.equalsIgnoreCase("setwithJavaType")) + sqlPstmt.setObject(i, (DateTimeOffset) dateValues.get(2), microsoft.sql.Types.DATETIMEOFFSET); + else if (setter.equalsIgnoreCase("setwithJDBCType")) + sqlPstmt.setObject(i, (DateTimeOffset) dateValues.get(2), microsoft.sql.Types.DATETIMEOFFSET); + else + sqlPstmt.setObject(i, (DateTimeOffset) dateValues.get(2)); + } + + // time default + for (int i = 10; i <= 12; i++) { + if (setter.equalsIgnoreCase("setwithJavaType")) + sqlPstmt.setObject(i, (Time) dateValues.get(3), java.sql.Types.TIME); + else if (setter.equalsIgnoreCase("setwithJDBCType")) + sqlPstmt.setObject(i, (Time) dateValues.get(3), JDBCType.TIME); + else + sqlPstmt.setObject(i, (Time) dateValues.get(3)); + } + + // datetime + for (int i = 13; i <= 15; i++) { + sqlPstmt.setObject(i, (Timestamp) dateValues.get(4), microsoft.sql.Types.DATETIME); + } + + // smalldatetime + for (int i = 16; i <= 18; i++) { + sqlPstmt.setObject(i, (Timestamp) dateValues.get(5), microsoft.sql.Types.SMALLDATETIME); + } + + sqlPstmt.execute(); + Util.close(null, sqlPstmt, null); + } + + private void populateDateSetObjectNull() throws SQLException { + String sql = "insert into " + dateTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?" + ")"; + + SQLServerPreparedStatement sqlPstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + + // date + for (int i = 1; i <= 3; i++) { + sqlPstmt.setObject(i, null, java.sql.Types.DATE); + } + + // datetime2 default + for (int i = 4; i <= 6; i++) { + sqlPstmt.setObject(i, null, java.sql.Types.TIMESTAMP); + } + + // datetimeoffset default + for (int i = 7; i <= 9; i++) { + sqlPstmt.setObject(i, null, microsoft.sql.Types.DATETIMEOFFSET); + } + + // time default + for (int i = 10; i <= 12; i++) { + sqlPstmt.setObject(i, null, java.sql.Types.TIME); + } + + // datetime + for (int i = 13; i <= 15; i++) { + sqlPstmt.setObject(i, null, microsoft.sql.Types.DATETIME); + } + + // smalldatetime + for (int i = 16; i <= 18; i++) { + sqlPstmt.setObject(i, null, microsoft.sql.Types.SMALLDATETIME); + } + + sqlPstmt.execute(); + Util.close(null, sqlPstmt, null); + } + + private void populateDateNullCase() throws SQLException { + String sql = "insert into " + dateTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?" + ")"; + + SQLServerPreparedStatement sqlPstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + + // date + for (int i = 1; i <= 3; i++) { + sqlPstmt.setNull(i, java.sql.Types.DATE); + } + + // datetime2 default + for (int i = 4; i <= 6; i++) { + sqlPstmt.setNull(i, java.sql.Types.TIMESTAMP); + } + + // datetimeoffset default + for (int i = 7; i <= 9; i++) { + sqlPstmt.setNull(i, microsoft.sql.Types.DATETIMEOFFSET); + } + + // time default + for (int i = 10; i <= 12; i++) { + sqlPstmt.setNull(i, java.sql.Types.TIME); + } + + // datetime + for (int i = 13; i <= 15; i++) { + sqlPstmt.setNull(i, microsoft.sql.Types.DATETIME); + } + + // smalldatetime + for (int i = 16; i <= 18; i++) { + sqlPstmt.setNull(i, microsoft.sql.Types.SMALLDATETIME); + } + + sqlPstmt.execute(); + Util.close(null, sqlPstmt, null); + } + + /** + * Populating the table + * + * @param values + * @throws SQLException + */ + private void populateNumeric(String[] values) throws SQLException { + String sql = "insert into " + numericTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?" + ")"; + + pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + + // bit + for (int i = 1; i <= 3; i++) { + if (values[0].equalsIgnoreCase("true")) { + pstmt.setBoolean(i, true); + } + else { + pstmt.setBoolean(i, false); + } + } + + // tinyint + for (int i = 4; i <= 6; i++) { + pstmt.setShort(i, Short.valueOf(values[1])); + } + + // smallint + for (int i = 7; i <= 9; i++) { + pstmt.setShort(i, Short.valueOf(values[2])); + } + + // int + for (int i = 10; i <= 12; i++) { + pstmt.setInt(i, Integer.valueOf(values[3])); + } + + // bigint + for (int i = 13; i <= 15; i++) { + pstmt.setLong(i, Long.valueOf(values[4])); + } + + // float default + for (int i = 16; i <= 18; i++) { + pstmt.setDouble(i, Double.valueOf(values[5])); + } + + // float(30) + for (int i = 19; i <= 21; i++) { + pstmt.setDouble(i, Double.valueOf(values[6])); + } + + // real + for (int i = 22; i <= 24; i++) { + pstmt.setFloat(i, Float.valueOf(values[7])); + } + + // decimal default + for (int i = 25; i <= 27; i++) { + if (values[8].equalsIgnoreCase("0")) + pstmt.setBigDecimal(i, new BigDecimal(values[8]), 18, 0); + else + pstmt.setBigDecimal(i, new BigDecimal(values[8])); + } + + // decimal(10,5) + for (int i = 28; i <= 30; i++) { + pstmt.setBigDecimal(i, new BigDecimal(values[9]), 10, 5); + } + + // numeric + for (int i = 31; i <= 33; i++) { + if (values[10].equalsIgnoreCase("0")) + pstmt.setBigDecimal(i, new BigDecimal(values[10]), 18, 0); + else + pstmt.setBigDecimal(i, new BigDecimal(values[10])); + } + + // numeric(8,2) + for (int i = 34; i <= 36; i++) { + pstmt.setBigDecimal(i, new BigDecimal(values[11]), 8, 2); + } + + // small money + for (int i = 37; i <= 39; i++) { + pstmt.setSmallMoney(i, new BigDecimal(values[12])); + } + + // money + for (int i = 40; i <= 42; i++) { + pstmt.setMoney(i, new BigDecimal(values[13])); + } + + // decimal(28,4) + for (int i = 43; i <= 45; i++) { + pstmt.setBigDecimal(i, new BigDecimal(values[14]), 28, 4); + } + + // numeric(28,4) + for (int i = 46; i <= 48; i++) { + pstmt.setBigDecimal(i, new BigDecimal(values[15]), 28, 4); + } + + pstmt.execute(); + Util.close(null, pstmt, null); + } + + private void populateNumericSetObject(String[] values) throws SQLException { + String sql = "insert into " + numericTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?" + ")"; + + pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + + // bit + for (int i = 1; i <= 3; i++) { + if (values[0].equalsIgnoreCase("true")) { + pstmt.setObject(i, true); + } + else { + pstmt.setObject(i, false); + } + } + + // tinyint + for (int i = 4; i <= 6; i++) { + pstmt.setObject(i, Short.valueOf(values[1])); + } + + // smallint + for (int i = 7; i <= 9; i++) { + pstmt.setObject(i, Short.valueOf(values[2])); + } + + // int + for (int i = 10; i <= 12; i++) { + pstmt.setObject(i, Integer.valueOf(values[3])); + } + + // bigint + for (int i = 13; i <= 15; i++) { + pstmt.setObject(i, Long.valueOf(values[4])); + } + + // float default + for (int i = 16; i <= 18; i++) { + pstmt.setObject(i, Double.valueOf(values[5])); + } + + // float(30) + for (int i = 19; i <= 21; i++) { + pstmt.setObject(i, Double.valueOf(values[6])); + } + + // real + for (int i = 22; i <= 24; i++) { + pstmt.setObject(i, Float.valueOf(values[7])); + } + + // decimal default + for (int i = 25; i <= 27; i++) { + if (RandomData.returnZero) + pstmt.setObject(i, new BigDecimal(values[8]), java.sql.Types.DECIMAL, 18, 0); + else + pstmt.setObject(i, new BigDecimal(values[8])); + } + + // decimal(10,5) + for (int i = 28; i <= 30; i++) { + pstmt.setObject(i, new BigDecimal(values[9]), java.sql.Types.DECIMAL, 10, 5); + } + + // numeric + for (int i = 31; i <= 33; i++) { + if (RandomData.returnZero) + pstmt.setObject(i, new BigDecimal(values[10]), java.sql.Types.NUMERIC, 18, 0); + else + pstmt.setObject(i, new BigDecimal(values[10])); + } + + // numeric(8,2) + for (int i = 34; i <= 36; i++) { + pstmt.setObject(i, new BigDecimal(values[11]), java.sql.Types.NUMERIC, 8, 2); + } + + // small money + for (int i = 37; i <= 39; i++) { + pstmt.setObject(i, new BigDecimal(values[12]), microsoft.sql.Types.SMALLMONEY); + } + + // money + for (int i = 40; i <= 42; i++) { + pstmt.setObject(i, new BigDecimal(values[13]), microsoft.sql.Types.MONEY); + } + + // decimal(28,4) + for (int i = 43; i <= 45; i++) { + pstmt.setObject(i, new BigDecimal(values[14]), java.sql.Types.DECIMAL, 28, 4); + } + + // numeric + for (int i = 46; i <= 48; i++) { + pstmt.setObject(i, new BigDecimal(values[15]), java.sql.Types.NUMERIC, 28, 4); + } + + pstmt.execute(); + Util.close(null, pstmt, null); + } + + private void populateNumericSetObjectWithJDBCTypes(String[] values) throws SQLException { + String sql = "insert into " + numericTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?" + ")"; + + pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + + // bit + for (int i = 1; i <= 3; i++) { + if (values[0].equalsIgnoreCase("true")) { + pstmt.setObject(i, true); + } + else { + pstmt.setObject(i, false); + } + } + + // tinyint + for (int i = 4; i <= 6; i++) { + pstmt.setObject(i, Short.valueOf(values[1]), JDBCType.TINYINT); + } + + // smallint + for (int i = 7; i <= 9; i++) { + pstmt.setObject(i, Short.valueOf(values[2]), JDBCType.SMALLINT); + } + + // int + for (int i = 10; i <= 12; i++) { + pstmt.setObject(i, Integer.valueOf(values[3]), JDBCType.INTEGER); + } + + // bigint + for (int i = 13; i <= 15; i++) { + pstmt.setObject(i, Long.valueOf(values[4]), JDBCType.BIGINT); + } + + // float default + for (int i = 16; i <= 18; i++) { + pstmt.setObject(i, Double.valueOf(values[5]), JDBCType.DOUBLE); + } + + // float(30) + for (int i = 19; i <= 21; i++) { + pstmt.setObject(i, Double.valueOf(values[6]), JDBCType.DOUBLE); + } + + // real + for (int i = 22; i <= 24; i++) { + pstmt.setObject(i, Float.valueOf(values[7]), JDBCType.REAL); + } + + // decimal default + for (int i = 25; i <= 27; i++) { + if (RandomData.returnZero) + pstmt.setObject(i, new BigDecimal(values[8]), java.sql.Types.DECIMAL, 18, 0); + else + pstmt.setObject(i, new BigDecimal(values[8])); + } + + // decimal(10,5) + for (int i = 28; i <= 30; i++) { + pstmt.setObject(i, new BigDecimal(values[9]), java.sql.Types.DECIMAL, 10, 5); + } + + // numeric + for (int i = 31; i <= 33; i++) { + if (RandomData.returnZero) + pstmt.setObject(i, new BigDecimal(values[10]), java.sql.Types.NUMERIC, 18, 0); + else + pstmt.setObject(i, new BigDecimal(values[10])); + } + + // numeric(8,2) + for (int i = 34; i <= 36; i++) { + pstmt.setObject(i, new BigDecimal(values[11]), java.sql.Types.NUMERIC, 8, 2); + } + + // small money + for (int i = 37; i <= 39; i++) { + pstmt.setObject(i, new BigDecimal(values[12]), microsoft.sql.Types.SMALLMONEY); + } + + // money + for (int i = 40; i <= 42; i++) { + pstmt.setObject(i, new BigDecimal(values[13]), microsoft.sql.Types.MONEY); + } + + // decimal(28,4) + for (int i = 43; i <= 45; i++) { + pstmt.setObject(i, new BigDecimal(values[14]), java.sql.Types.DECIMAL, 28, 4); + } + + // numeric + for (int i = 46; i <= 48; i++) { + pstmt.setObject(i, new BigDecimal(values[15]), java.sql.Types.NUMERIC, 28, 4); + } + + pstmt.execute(); + Util.close(null, pstmt, null); + } + + private void populateNumericSetObjectNull() throws SQLException { + String sql = "insert into " + numericTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?" + ")"; + + pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + + // bit + for (int i = 1; i <= 3; i++) { + pstmt.setObject(i, null, java.sql.Types.BIT); + } + + // tinyint + for (int i = 4; i <= 6; i++) { + pstmt.setObject(i, null, java.sql.Types.TINYINT); + } + + // smallint + for (int i = 7; i <= 9; i++) { + pstmt.setObject(i, null, java.sql.Types.SMALLINT); + } + + // int + for (int i = 10; i <= 12; i++) { + pstmt.setObject(i, null, java.sql.Types.INTEGER); + } + + // bigint + for (int i = 13; i <= 15; i++) { + pstmt.setObject(i, null, java.sql.Types.BIGINT); + } + + // float default + for (int i = 16; i <= 18; i++) { + pstmt.setObject(i, null, java.sql.Types.DOUBLE); + } + + // float(30) + for (int i = 19; i <= 21; i++) { + pstmt.setObject(i, null, java.sql.Types.DOUBLE); + } + + // real + for (int i = 22; i <= 24; i++) { + pstmt.setObject(i, null, java.sql.Types.REAL); + } + + // decimal default + for (int i = 25; i <= 27; i++) { + pstmt.setObject(i, null, java.sql.Types.DECIMAL); + } + + // decimal(10,5) + for (int i = 28; i <= 30; i++) { + pstmt.setObject(i, null, java.sql.Types.DECIMAL, 10, 5); + } + + // numeric + for (int i = 31; i <= 33; i++) { + pstmt.setObject(i, null, java.sql.Types.NUMERIC); + } + + // numeric(8,2) + for (int i = 34; i <= 36; i++) { + pstmt.setObject(i, null, java.sql.Types.NUMERIC, 8, 2); + } + + // small money + for (int i = 37; i <= 39; i++) { + pstmt.setObject(i, null, microsoft.sql.Types.SMALLMONEY); + } + + // money + for (int i = 40; i <= 42; i++) { + pstmt.setObject(i, null, microsoft.sql.Types.MONEY); + } + + // decimal(28,4) + for (int i = 43; i <= 45; i++) { + pstmt.setObject(i, null, java.sql.Types.DECIMAL, 28, 4); + } + + // numeric + for (int i = 46; i <= 48; i++) { + pstmt.setObject(i, null, java.sql.Types.NUMERIC, 28, 4); + } + + pstmt.execute(); + Util.close(null, pstmt, null); + } + + private void populateNumericNullCase(String[] values) throws SQLException { + String sql = "insert into " + numericTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?" + + + ")"; + + pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + + // bit + for (int i = 1; i <= 3; i++) { + pstmt.setNull(i, java.sql.Types.BIT); + } + + // tinyint + for (int i = 4; i <= 6; i++) { + pstmt.setNull(i, java.sql.Types.TINYINT); + } + + // smallint + for (int i = 7; i <= 9; i++) { + pstmt.setNull(i, java.sql.Types.SMALLINT); + } + + // int + for (int i = 10; i <= 12; i++) { + pstmt.setNull(i, java.sql.Types.INTEGER); + } + + // bigint + for (int i = 13; i <= 15; i++) { + pstmt.setNull(i, java.sql.Types.BIGINT); + } + + // float default + for (int i = 16; i <= 18; i++) { + pstmt.setNull(i, java.sql.Types.DOUBLE); + } + + // float(30) + for (int i = 19; i <= 21; i++) { + pstmt.setNull(i, java.sql.Types.DOUBLE); + } + + // real + for (int i = 22; i <= 24; i++) { + pstmt.setNull(i, java.sql.Types.REAL); + } + + // decimal default + for (int i = 25; i <= 27; i++) { + pstmt.setBigDecimal(i, null); + } + + // decimal(10,5) + for (int i = 28; i <= 30; i++) { + pstmt.setBigDecimal(i, null, 10, 5); + } + + // numeric + for (int i = 31; i <= 33; i++) { + pstmt.setBigDecimal(i, null); + } + + // numeric(8,2) + for (int i = 34; i <= 36; i++) { + pstmt.setBigDecimal(i, null, 8, 2); + } + + // small money + for (int i = 37; i <= 39; i++) { + pstmt.setSmallMoney(i, null); + } + + // money + for (int i = 40; i <= 42; i++) { + pstmt.setMoney(i, null); + } + + // decimal(28,4) + for (int i = 43; i <= 45; i++) { + pstmt.setBigDecimal(i, null, 28, 4); + } + + // decimal(28,4) + for (int i = 46; i <= 48; i++) { + pstmt.setBigDecimal(i, null, 28, 4); + } + pstmt.execute(); + Util.close(null, pstmt, null); + } + + private void populateNumericNormalization(String[] numericValues) throws SQLException { + String sql = "insert into " + numericTable + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?" + + + ")"; + + pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + + // bit + for (int i = 1; i <= 3; i++) { + if (numericValues[0].equalsIgnoreCase("true")) { + pstmt.setBoolean(i, true); + } + else { + pstmt.setBoolean(i, false); + } + } + + // tinyint + for (int i = 4; i <= 6; i++) { + if (1 == Integer.valueOf(numericValues[1])) { + pstmt.setBoolean(i, true); + } + else { + pstmt.setBoolean(i, false); + } + } + + // smallint + for (int i = 7; i <= 9; i++) { + if (numericValues[2].equalsIgnoreCase("255")) { + pstmt.setByte(i, (byte) 255); + } + else { + pstmt.setByte(i, Byte.valueOf(numericValues[2])); + } + } + + // int + for (int i = 10; i <= 12; i++) { + pstmt.setShort(i, Short.valueOf(numericValues[3])); + } + + // bigint + for (int i = 13; i <= 15; i++) { + pstmt.setInt(i, Integer.valueOf(numericValues[4])); + } + + // float default + for (int i = 16; i <= 18; i++) { + pstmt.setDouble(i, Double.valueOf(numericValues[5])); + } + + // float(30) + for (int i = 19; i <= 21; i++) { + pstmt.setDouble(i, Double.valueOf(numericValues[6])); + } + + // real + for (int i = 22; i <= 24; i++) { + pstmt.setFloat(i, Float.valueOf(numericValues[7])); + } + + // decimal default + for (int i = 25; i <= 27; i++) { + pstmt.setBigDecimal(i, new BigDecimal(numericValues[8])); + } + + // decimal(10,5) + for (int i = 28; i <= 30; i++) { + pstmt.setBigDecimal(i, new BigDecimal(numericValues[9]), 10, 5); + } + + // numeric + for (int i = 31; i <= 33; i++) { + pstmt.setBigDecimal(i, new BigDecimal(numericValues[10])); + } + + // numeric(8,2) + for (int i = 34; i <= 36; i++) { + pstmt.setBigDecimal(i, new BigDecimal(numericValues[11]), 8, 2); + } + + // small money + for (int i = 37; i <= 39; i++) { + pstmt.setSmallMoney(i, new BigDecimal(numericValues[12])); + } + + // money + for (int i = 40; i <= 42; i++) { + pstmt.setSmallMoney(i, new BigDecimal(numericValues[13])); + } + + // decimal(28,4) + for (int i = 43; i <= 45; i++) { + pstmt.setBigDecimal(i, new BigDecimal(numericValues[14]), 28, 4); + } + + // numeric + for (int i = 46; i <= 48; i++) { + pstmt.setBigDecimal(i, new BigDecimal(numericValues[15]), 28, 4); + } + + pstmt.execute(); + Util.close(null, pstmt, null); + } + + private void testChar(SQLServerStatement stmt, + String[] values) throws SQLException { + String sql = "select * from " + charTable; + SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + ResultSet rs = null; + if (stmt == null) { + rs = pstmt.executeQuery(); + } + else { + rs = stmt.executeQuery(sql); + } + int numberOfColumns = rs.getMetaData().getColumnCount(); + + while (rs.next()) { + testGetString(rs, numberOfColumns, values); + testGetObject(rs, numberOfColumns, values); + } + + Util.close(rs, pstmt, null); + } + + private void testBinary(SQLServerStatement stmt, + LinkedList values) throws SQLException { + String sql = "select * from " + binaryTable; + SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + ResultSet rs = null; + if (stmt == null) { + rs = pstmt.executeQuery(); + } + else { + rs = stmt.executeQuery(sql); + } + int numberOfColumns = rs.getMetaData().getColumnCount(); + + while (rs.next()) { + testGetStringForBinary(rs, numberOfColumns, values); + testGetBytes(rs, numberOfColumns, values); + testGetObjectForBinary(rs, numberOfColumns, values); + } + + Util.close(rs, pstmt, null); + } + + private void testDate(SQLServerStatement stmt, + LinkedList values1) throws SQLException { + + String sql = "select * from " + dateTable; + SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + ResultSet rs = null; + if (stmt == null) { + rs = pstmt.executeQuery(); + } + else { + rs = stmt.executeQuery(sql); + } + int numberOfColumns = rs.getMetaData().getColumnCount(); + + while (rs.next()) { + // testGetStringForDate(rs, numberOfColumns, values1); //TODO: Disabling, since getString throws verification error for zero temporal + // types + testGetObjectForTemporal(rs, numberOfColumns, values1); + testGetDate(rs, numberOfColumns, values1); + } + + Util.close(rs, pstmt, null); + } + + private void testGetObject(ResultSet rs, + int numberOfColumns, + String[] values) throws SQLException { + int index = 0; + for (int i = 1; i <= numberOfColumns; i = i + 3) { + try { + String objectValue1 = ("" + rs.getObject(i)).trim(); + String objectValue2 = ("" + rs.getObject(i + 1)).trim(); + String objectValue3 = ("" + rs.getObject(i + 2)).trim(); + + boolean matches = objectValue1.equalsIgnoreCase("" + values[index]) && objectValue2.equalsIgnoreCase("" + values[index]) + && objectValue3.equalsIgnoreCase("" + values[index]); + + if (("" + values[index]).length() >= 1000) { + assertTrue(matches, "\nDecryption failed with getObject() at index: " + i + ", " + (i + 1) + ", " + (i + 2) + + ".\nExpected Value at index: " + index); + } + else { + assertTrue(matches, "\nDecryption failed with getObject(): " + objectValue1 + ", " + objectValue2 + ", " + objectValue3 + + ".\nExpected Value: " + values[index]); + } + } + finally { + index++; + } + } + } + + private void testGetObjectForTemporal(ResultSet rs, + int numberOfColumns, + LinkedList values) throws SQLException { + int index = 0; + for (int i = 1; i <= numberOfColumns; i = i + 3) { + try { + String objectValue1 = ("" + rs.getObject(i)).trim(); + String objectValue2 = ("" + rs.getObject(i + 1)).trim(); + String objectValue3 = ("" + rs.getObject(i + 2)).trim(); + + Object expected = null; + if (rs.getMetaData().getColumnTypeName(i).equalsIgnoreCase("smalldatetime")) { + expected = Util.roundSmallDateTimeValue(values.get(index)); + } + else if (rs.getMetaData().getColumnTypeName(i).equalsIgnoreCase("datetime")) { + expected = Util.roundDatetimeValue(values.get(index)); + } + else { + expected = values.get(index); + } + assertTrue( + objectValue1.equalsIgnoreCase("" + expected) && objectValue2.equalsIgnoreCase("" + expected) + && objectValue3.equalsIgnoreCase("" + expected), + "\nDecryption failed with getObject(): " + objectValue1 + ", " + objectValue2 + ", " + objectValue3 + ".\nExpected Value: " + + expected); + } + finally { + index++; + } + } + } + + private void testGetObjectForBinary(ResultSet rs, + int numberOfColumns, + LinkedList values) throws SQLException { + int index = 0; + for (int i = 1; i <= numberOfColumns; i = i + 3) { + byte[] objectValue1 = (byte[]) rs.getObject(i); + byte[] objectValue2 = (byte[]) rs.getObject(i + 1); + byte[] objectValue3 = (byte[]) rs.getObject(i + 2); + + byte[] expectedBytes = null; + + if (null != values.get(index)) { + expectedBytes = values.get(index); + } + + try { + if (null != values.get(index)) { + for (int j = 0; j < expectedBytes.length; j++) { + assertTrue(expectedBytes[j] == objectValue1[j] && expectedBytes[j] == objectValue2[j] && expectedBytes[j] == objectValue3[j], + "Decryption failed with getObject(): " + objectValue1 + ", " + objectValue2 + ", " + objectValue3 + ".\n"); + } + } + } + finally { + index++; + } + } + } + + private void testGetBigDecimal(ResultSet rs, + int numberOfColumns, + String[] values) throws SQLException { + + int index = 0; + for (int i = 1; i <= numberOfColumns; i = i + 3) { + + String decimalValue1 = "" + rs.getBigDecimal(i); + String decimalValue2 = "" + rs.getBigDecimal(i + 1); + String decimalValue3 = "" + rs.getBigDecimal(i + 2); + + if (decimalValue1.equalsIgnoreCase("0") && (values[index].equalsIgnoreCase("true") || values[index].equalsIgnoreCase("false"))) { + decimalValue1 = "false"; + decimalValue2 = "false"; + decimalValue3 = "false"; + } + else if (decimalValue1.equalsIgnoreCase("1") && (values[index].equalsIgnoreCase("true") || values[index].equalsIgnoreCase("false"))) { + decimalValue1 = "true"; + decimalValue2 = "true"; + decimalValue3 = "true"; + } + + if (null != values[index]) { + if (values[index].equalsIgnoreCase("1.79E308")) { + values[index] = "1.79E+308"; + } + else if (values[index].equalsIgnoreCase("3.4E38")) { + values[index] = "3.4E+38"; + } + + if (values[index].equalsIgnoreCase("-1.79E308")) { + values[index] = "-1.79E+308"; + } + else if (values[index].equalsIgnoreCase("-3.4E38")) { + values[index] = "-3.4E+38"; + } + } + + try { + assertTrue( + decimalValue1.equalsIgnoreCase("" + values[index]) && decimalValue2.equalsIgnoreCase("" + values[index]) + && decimalValue3.equalsIgnoreCase("" + values[index]), + "\nDecryption failed with getBigDecimal(): " + decimalValue1 + ", " + decimalValue2 + ", " + decimalValue3 + + ".\nExpected Value: " + values[index]); + } + finally { + index++; + } + } + } + + private void testGetString(ResultSet rs, + int numberOfColumns, + String[] values) throws SQLException { + + int index = 0; + for (int i = 1; i <= numberOfColumns; i = i + 3) { + String stringValue1 = ("" + rs.getString(i)).trim(); + String stringValue2 = ("" + rs.getString(i + 1)).trim(); + String stringValue3 = ("" + rs.getString(i + 2)).trim(); + + if (stringValue1.equalsIgnoreCase("0") && (values[index].equalsIgnoreCase("true") || values[index].equalsIgnoreCase("false"))) { + stringValue1 = "false"; + stringValue2 = "false"; + stringValue3 = "false"; + } + else if (stringValue1.equalsIgnoreCase("1") && (values[index].equalsIgnoreCase("true") || values[index].equalsIgnoreCase("false"))) { + stringValue1 = "true"; + stringValue2 = "true"; + stringValue3 = "true"; + } + try { + + boolean matches = stringValue1.equalsIgnoreCase("" + values[index]) && stringValue2.equalsIgnoreCase("" + values[index]) + && stringValue3.equalsIgnoreCase("" + values[index]); + + if (("" + values[index]).length() >= 1000) { + assertTrue(matches, "\nDecryption failed with getString() at index: " + i + ", " + (i + 1) + ", " + (i + 2) + + ".\nExpected Value at index: " + index); + + } + else { + assertTrue(matches, "\nDecryption failed with getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + + ".\nExpected Value: " + values[index]); + } + } + finally { + index++; + } + } + } + + // not testing this for now. + @SuppressWarnings("unused") + private void testGetStringForDate(ResultSet rs, + int numberOfColumns, + LinkedList values) throws SQLException { + + int index = 0; + for (int i = 1; i <= numberOfColumns; i = i + 3) { + String stringValue1 = ("" + rs.getString(i)).trim(); + String stringValue2 = ("" + rs.getString(i + 1)).trim(); + String stringValue3 = ("" + rs.getString(i + 2)).trim(); + + try { + if (index == 3) { + assertTrue( + stringValue1.contains("" + values.get(index)) && stringValue2.contains("" + values.get(index)) + && stringValue3.contains("" + values.get(index)), + "\nDecryption failed with getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + + ".\nExpected Value: " + values.get(index)); + } + else if (index == 4) // round value for datetime + { + Object datetimeValue = "" + Util.roundDatetimeValue(values.get(index)); + assertTrue( + stringValue1.equalsIgnoreCase("" + datetimeValue) && stringValue2.equalsIgnoreCase("" + datetimeValue) + && stringValue3.equalsIgnoreCase("" + datetimeValue), + "\nDecryption failed with getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + + ".\nExpected Value: " + datetimeValue); + } + else if (index == 5) // round value for smalldatetime + { + Object smalldatetimeValue = "" + Util.roundSmallDateTimeValue(values.get(index)); + assertTrue( + stringValue1.equalsIgnoreCase("" + smalldatetimeValue) && stringValue2.equalsIgnoreCase("" + smalldatetimeValue) + && stringValue3.equalsIgnoreCase("" + smalldatetimeValue), + "\nDecryption failed with getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + + ".\nExpected Value: " + smalldatetimeValue); + } + else { + assertTrue( + stringValue1.contains("" + values.get(index)) && stringValue2.contains("" + values.get(index)) + && stringValue3.contains("" + values.get(index)), + "\nDecryption failed with getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + + ".\nExpected Value: " + values.get(index)); + } + } + finally { + index++; + } + } + } + + private void testGetBytes(ResultSet rs, + int numberOfColumns, + LinkedList values) throws SQLException { + int index = 0; + for (int i = 1; i <= numberOfColumns; i = i + 3) { + byte[] b1 = rs.getBytes(i); + byte[] b2 = rs.getBytes(i + 1); + byte[] b3 = rs.getBytes(i + 2); + + byte[] expectedBytes = null; + + if (null != values.get(index)) { + expectedBytes = values.get(index); + } + + try { + if (null != values.get(index)) { + for (int j = 0; j < expectedBytes.length; j++) { + assertTrue(expectedBytes[j] == b1[j] && expectedBytes[j] == b2[j] && expectedBytes[j] == b3[j], + "Decryption failed with getObject(): " + b1 + ", " + b2 + ", " + b3 + ".\n"); + } + } + } + finally { + index++; + } + } + } + + private void testGetStringForBinary(ResultSet rs, + int numberOfColumns, + LinkedList values) throws SQLException { + + int index = 0; + for (int i = 1; i <= numberOfColumns; i = i + 3) { + String stringValue1 = ("" + rs.getString(i)).trim(); + String stringValue2 = ("" + rs.getString(i + 1)).trim(); + String stringValue3 = ("" + rs.getString(i + 2)).trim(); + + StringBuffer expected = new StringBuffer(); + String expectedStr = null; + + if (null != values.get(index)) { + for (byte b : values.get(index)) { + expected.append(String.format("%02X", b)); + } + expectedStr = "" + expected.toString(); + } + else { + expectedStr = "null"; + } + + try { + assertTrue(stringValue1.startsWith(expectedStr) && stringValue2.startsWith(expectedStr) && stringValue3.startsWith(expectedStr), + "\nDecryption failed with getString(): " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + ".\nExpected Value: " + + expectedStr); + } + finally { + index++; + } + } + } + + private void testGetDate(ResultSet rs, + int numberOfColumns, + LinkedList values) throws SQLException { + for (int i = 1; i <= numberOfColumns; i = i + 3) { + + if (rs instanceof SQLServerResultSet) { + + String stringValue1 = null; + String stringValue2 = null; + String stringValue3 = null; + String expected = null; + + switch (i) { + + case 1: + stringValue1 = "" + ((SQLServerResultSet) rs).getDate(i); + stringValue2 = "" + ((SQLServerResultSet) rs).getDate(i + 1); + stringValue3 = "" + ((SQLServerResultSet) rs).getDate(i + 2); + expected = "" + values.get(0); + break; + + case 4: + stringValue1 = "" + ((SQLServerResultSet) rs).getTimestamp(i); + stringValue2 = "" + ((SQLServerResultSet) rs).getTimestamp(i + 1); + stringValue3 = "" + ((SQLServerResultSet) rs).getTimestamp(i + 2); + expected = "" + values.get(1); + break; + + case 7: + stringValue1 = "" + ((SQLServerResultSet) rs).getDateTimeOffset(i); + stringValue2 = "" + ((SQLServerResultSet) rs).getDateTimeOffset(i + 1); + stringValue3 = "" + ((SQLServerResultSet) rs).getDateTimeOffset(i + 2); + expected = "" + values.get(2); + break; + + case 10: + stringValue1 = "" + ((SQLServerResultSet) rs).getTime(i); + stringValue2 = "" + ((SQLServerResultSet) rs).getTime(i + 1); + stringValue3 = "" + ((SQLServerResultSet) rs).getTime(i + 2); + expected = "" + values.get(3); + break; + + case 13: + stringValue1 = "" + ((SQLServerResultSet) rs).getDateTime(i); + stringValue2 = "" + ((SQLServerResultSet) rs).getDateTime(i + 1); + stringValue3 = "" + ((SQLServerResultSet) rs).getDateTime(i + 2); + expected = "" + Util.roundDatetimeValue(values.get(4)); + break; + + case 16: + stringValue1 = "" + ((SQLServerResultSet) rs).getSmallDateTime(i); + stringValue2 = "" + ((SQLServerResultSet) rs).getSmallDateTime(i + 1); + stringValue3 = "" + ((SQLServerResultSet) rs).getSmallDateTime(i + 2); + expected = "" + Util.roundSmallDateTimeValue(values.get(5)); + break; + + default: + fail("Switch case is not matched with data"); + } + + assertTrue( + stringValue1.equalsIgnoreCase(expected) && stringValue2.equalsIgnoreCase(expected) && stringValue3.equalsIgnoreCase(expected), + "\nDecryption failed with testGetDate: " + stringValue1 + ", " + stringValue2 + ", " + stringValue3 + ".\nExpected Value: " + + expected); + } + + else { + fail("Result set is not instance of SQLServerResultSet"); + } + } + } + + private void testNumeric(Statement stmt, + String[] numericValues, + boolean isNull) throws SQLException { + String sql = "select * from " + numericTable; + SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) Util.getPreparedStmt(con, sql, stmtColEncSetting); + SQLServerResultSet rs = null; + if (stmt == null) { + rs = (SQLServerResultSet) pstmt.executeQuery(); + } + else { + rs = (SQLServerResultSet) stmt.executeQuery(sql); + } + int numberOfColumns = rs.getMetaData().getColumnCount(); + + while (rs.next()) { + testGetString(rs, numberOfColumns, numericValues); + testGetObject(rs, numberOfColumns, numericValues); + testGetBigDecimal(rs, numberOfColumns, numericValues); + if (!isNull) + testWithSpecifiedtype(rs, numberOfColumns, numericValues); + else { + String[] nullNumericValues = {"false", "0", "0", "0", "0", "0.0", "0.0", "0.0", null, null, null, null, null, null, null, null}; + testWithSpecifiedtype(rs, numberOfColumns, nullNumericValues); + } + } + + Util.close(rs, pstmt, null); + } + + private void testWithSpecifiedtype(SQLServerResultSet rs, + int numberOfColumns, + String[] values) throws SQLException { + + String value1, value2, value3, expectedValue = null; + int index = 0; + + // bit + value1 = "" + rs.getBoolean(1); + value2 = "" + rs.getBoolean(2); + value3 = "" + rs.getBoolean(3); + + expectedValue = values[index]; + Compare(expectedValue, value1, value2, value3); + index++; + + // tiny + value1 = "" + rs.getShort(4); + value2 = "" + rs.getShort(5); + value3 = "" + rs.getShort(6); + + expectedValue = values[index]; + Compare(expectedValue, value1, value2, value3); + index++; + + // smallint + value1 = "" + rs.getShort(7); + value2 = "" + rs.getShort(8); + value3 = "" + rs.getShort(8); + + expectedValue = values[index]; + Compare(expectedValue, value1, value2, value3); + index++; + + // int + value1 = "" + rs.getInt(10); + value2 = "" + rs.getInt(11); + value3 = "" + rs.getInt(12); + + expectedValue = values[index]; + Compare(expectedValue, value1, value2, value3); + index++; + + // bigint + value1 = "" + rs.getLong(13); + value2 = "" + rs.getLong(14); + value3 = "" + rs.getLong(15); + + expectedValue = values[index]; + Compare(expectedValue, value1, value2, value3); + index++; + + // float + value1 = "" + rs.getDouble(16); + value2 = "" + rs.getDouble(17); + value3 = "" + rs.getDouble(18); + + expectedValue = values[index]; + Compare(expectedValue, value1, value2, value3); + index++; + + // float(30) + value1 = "" + rs.getDouble(19); + value2 = "" + rs.getDouble(20); + value3 = "" + rs.getDouble(21); + + expectedValue = values[index]; + Compare(expectedValue, value1, value2, value3); + index++; + + // real + value1 = "" + rs.getFloat(22); + value2 = "" + rs.getFloat(23); + value3 = "" + rs.getFloat(24); + + expectedValue = values[index]; + Compare(expectedValue, value1, value2, value3); + index++; + + // decimal + value1 = "" + rs.getBigDecimal(25); + value2 = "" + rs.getBigDecimal(26); + value3 = "" + rs.getBigDecimal(27); + + expectedValue = values[index]; + Compare(expectedValue, value1, value2, value3); + index++; + + // decimal (10,5) + value1 = "" + rs.getBigDecimal(28); + value2 = "" + rs.getBigDecimal(29); + value3 = "" + rs.getBigDecimal(30); + + expectedValue = values[index]; + Compare(expectedValue, value1, value2, value3); + index++; + + // numeric + value1 = "" + rs.getBigDecimal(31); + value2 = "" + rs.getBigDecimal(32); + value3 = "" + rs.getBigDecimal(33); + + expectedValue = values[index]; + Compare(expectedValue, value1, value2, value3); + index++; + + // numeric (8,2) + value1 = "" + rs.getBigDecimal(34); + value2 = "" + rs.getBigDecimal(35); + value3 = "" + rs.getBigDecimal(36); + + expectedValue = values[index]; + Compare(expectedValue, value1, value2, value3); + index++; + + // smallmoney + value1 = "" + rs.getSmallMoney(37); + value2 = "" + rs.getSmallMoney(38); + value3 = "" + rs.getSmallMoney(39); + + expectedValue = values[index]; + Compare(expectedValue, value1, value2, value3); + index++; + + // money + value1 = "" + rs.getMoney(40); + value2 = "" + rs.getMoney(41); + value3 = "" + rs.getMoney(42); + + expectedValue = values[index]; + Compare(expectedValue, value1, value2, value3); + index++; + + // decimal(28,4) + value1 = "" + rs.getBigDecimal(43); + value2 = "" + rs.getBigDecimal(44); + value3 = "" + rs.getBigDecimal(45); + + expectedValue = values[index]; + Compare(expectedValue, value1, value2, value3); + index++; + + // numeric(28,4) + value1 = "" + rs.getBigDecimal(46); + value2 = "" + rs.getBigDecimal(47); + value3 = "" + rs.getBigDecimal(48); + + expectedValue = values[index]; + Compare(expectedValue, value1, value2, value3); + index++; + } + + private void Compare(String expectedValue, + String value1, + String value2, + String value3) { + + if (null != expectedValue) { + if (expectedValue.equalsIgnoreCase("1.79E+308")) { + expectedValue = "1.79E308"; + } + else if (expectedValue.equalsIgnoreCase("3.4E+38")) { + expectedValue = "3.4E38"; + } + + if (expectedValue.equalsIgnoreCase("-1.79E+308")) { + expectedValue = "-1.79E308"; + } + else if (expectedValue.equalsIgnoreCase("-3.4E+38")) { + expectedValue = "-3.4E38"; + } + } + + assertTrue( + value1.equalsIgnoreCase("" + expectedValue) && value2.equalsIgnoreCase("" + expectedValue) + && value3.equalsIgnoreCase("" + expectedValue), + "\nDecryption failed with getBigDecimal(): " + value1 + ", " + value2 + ", " + value3 + ".\nExpected Value: " + expectedValue); + } + + private String[] createCharValues() { + + boolean encrypted = true; + String char20 = RandomData.generateCharTypes("20", nullable, encrypted); + String varchar50 = RandomData.generateCharTypes("50", nullable, encrypted); + String varcharmax = RandomData.generateCharTypes("max", nullable, encrypted); + String nchar30 = RandomData.generateNCharTypes("30", nullable, encrypted); + String nvarchar60 = RandomData.generateNCharTypes("60", nullable, encrypted); + String nvarcharmax = RandomData.generateNCharTypes("max", nullable, encrypted); + String varchar8000 = RandomData.generateCharTypes("8000", nullable, encrypted); + String nvarchar4000 = RandomData.generateNCharTypes("4000", nullable, encrypted); + + String[] values = {char20.trim(), varchar50, varcharmax, nchar30, nvarchar60, nvarcharmax, uid, varchar8000, nvarchar4000}; + + return values; + } + + private LinkedList createbinaryValues(boolean nullable) { + + boolean encrypted = true; + RandomData.returnNull = nullable; + + byte[] binary20 = RandomData.generateBinaryTypes("20", nullable, encrypted); + byte[] varbinary50 = RandomData.generateBinaryTypes("50", nullable, encrypted); + byte[] varbinarymax = RandomData.generateBinaryTypes("max", nullable, encrypted); + byte[] binary512 = RandomData.generateBinaryTypes("512", nullable, encrypted); + byte[] varbinary8000 = RandomData.generateBinaryTypes("8000", nullable, encrypted); + + LinkedList list = new LinkedList<>(); + list.add(binary20); + list.add(varbinary50); + list.add(varbinarymax); + list.add(binary512); + list.add(varbinary8000); + + return list; + } + + private String[] createNumericValues() { + + Boolean boolValue = RandomData.generateBoolean(nullable); + Short tinyIntValue = RandomData.generateTinyint(nullable); + Short smallIntValue = RandomData.generateSmallint(nullable); + Integer intValue = RandomData.generateInt(nullable); + Long bigintValue = RandomData.generateLong(nullable); + Double floatValue = RandomData.generateFloat(24, nullable); + Double floatValuewithPrecision = RandomData.generateFloat(53, nullable); + Float realValue = RandomData.generateReal(nullable); + BigDecimal decimal = RandomData.generateDecimalNumeric(18, 0, nullable); + BigDecimal decimalPrecisionScale = RandomData.generateDecimalNumeric(10, 5, nullable); + BigDecimal numeric = RandomData.generateDecimalNumeric(18, 0, nullable); + BigDecimal numericPrecisionScale = RandomData.generateDecimalNumeric(8, 2, nullable); + BigDecimal smallMoney = RandomData.generateSmallMoney(nullable); + BigDecimal money = RandomData.generateMoney(nullable); + BigDecimal decimalPrecisionScale2 = RandomData.generateDecimalNumeric(28, 4, nullable); + BigDecimal numericPrecisionScale2 = RandomData.generateDecimalNumeric(28, 4, nullable); + + String[] numericValues = {"" + boolValue, "" + tinyIntValue, "" + smallIntValue, "" + intValue, "" + bigintValue, "" + floatValue, + "" + floatValuewithPrecision, "" + realValue, "" + decimal, "" + decimalPrecisionScale, "" + numeric, "" + numericPrecisionScale, + "" + smallMoney, "" + money, "" + decimalPrecisionScale2, "" + numericPrecisionScale2}; + + return numericValues; + } + + private LinkedList createTemporalTypes() { + + Date date = RandomData.generateDate(nullable); + Timestamp datetime2 = RandomData.generateDatetime2(7, nullable); + DateTimeOffset datetimeoffset = RandomData.generateDatetimeoffset(7, nullable); + Time time = RandomData.generateTime(7, nullable); + Timestamp datetime = RandomData.generateDatetime(nullable); + Timestamp smalldatetime = RandomData.generateSmalldatetime(nullable); + + LinkedList list = new LinkedList<>(); + list.add(date); + list.add(datetime2); + list.add(datetimeoffset); + list.add(time); + list.add(datetime); + list.add(smalldatetime); + + return list; + } + + private void skipTestForJava7() throws TestAbortedException, SQLException { + assumeTrue(Util.supportJDBC42(con)); // With Java 7, skip tests for JDBCType. + } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/UtilTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/UtilTest.java new file mode 100644 index 0000000000..85f27900b0 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/UtilTest.java @@ -0,0 +1,32 @@ +/* + * 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 static org.junit.Assert.assertEquals; + +import java.util.UUID; + +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +/** + * Tests the Util class + * + */ +@RunWith(JUnitPlatform.class) +public class UtilTest { + + @Test + public void readGUIDtoUUID() throws SQLServerException { + UUID expected = UUID.fromString("6F9619FF-8B86-D011-B42D-00C04FC964FF"); + byte[] guid = new byte[] {-1, 25, -106, 111, -122, -117, 17, -48, -76, 45, 0, -64, 79, -55, 100, -1}; + assertEquals(expected, Util.readGUIDtoUUID(guid)); + } + +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BulkCopyWithSqlVariantTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BulkCopyWithSqlVariantTest.java new file mode 100644 index 0000000000..2f2c4b9b7d --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BulkCopyWithSqlVariantTest.java @@ -0,0 +1,717 @@ +/* + * 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.datatypes; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.math.BigDecimal; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import com.microsoft.sqlserver.jdbc.SQLServerBulkCopy; +import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.SQLServerResultSet; +import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.Utils; + +/** + * Test Bulkcopy with sql_variant datatype, testing all underlying supported datatypes + * + */ +@RunWith(JUnitPlatform.class) +public class BulkCopyWithSqlVariantTest extends AbstractTest { + + static SQLServerConnection con = null; + static Statement stmt = null; + static String tableName = "sqlVariantTestSrcTable"; + static String destTableName = "sqlVariantDestTable"; + static SQLServerResultSet rs = null; + + /** + * Test integer value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestInt() throws SQLException { + int col1Value = 5; + beforeEachSetup("int", col1Value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals(rs.getInt(1), 5); + } + } + + /** + * Test smallInt value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestSmallInt() throws SQLException { + int col1Value = 5; + beforeEachSetup("smallint", col1Value); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals(rs.getShort(1), 5); + } + bulkCopy.close(); + } + + /** + * Test tinyInt value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestTinyint() throws SQLException { + int col1Value = 5; + beforeEachSetup("tinyint", col1Value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals(rs.getByte(1), 5); + } + bulkCopy.close(); + } + + /** + * test Bigint value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestBigint() throws SQLException { + int col1Value = 5; + beforeEachSetup("bigint", col1Value); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals(rs.getLong(1), col1Value); + } + } + + /** + * test float value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestFloat() throws SQLException { + int col1Value = 5; + beforeEachSetup("float", col1Value); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals(rs.getDouble(1), col1Value); + } + } + + /** + * test real value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestReal() throws SQLException { + int col1Value = 5; + beforeEachSetup("real", col1Value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals(rs.getFloat(1), col1Value); + } + + } + + /** + * test money value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestMoney() throws SQLException { + String col1Value = "126.1230"; + beforeEachSetup("money", col1Value); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals(rs.getMoney(1), new BigDecimal(col1Value)); + } + + } + + /** + * test smallmoney + * + * @throws SQLException + */ + @Test + public void bulkCopyTestSmallmoney() throws SQLException { + String col1Value = "126.1230"; + String destTableName = "dest_sqlVariant"; + Utils.dropTableIfExists(tableName, stmt); + Utils.dropTableIfExists(destTableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant)"); + stmt.executeUpdate("INSERT into " + tableName + "(col1) values (CAST (" + col1Value + " AS " + "smallmoney" + ") )"); + stmt.executeUpdate("create table " + destTableName + " (col1 sql_variant)"); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals(rs.getSmallMoney(1), new BigDecimal(col1Value)); + } + + } + + /** + * test date value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestDate() throws SQLException { + String col1Value = "2015-05-05"; + beforeEachSetup("date", "'" + col1Value + "'"); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals("" + rs.getDate(1), col1Value); + } + + } + + /** + * Test bulkcoping two column with sql_variant datatype + * + * @throws SQLException + */ + @Test + public void bulkCopyTestTwoCols() throws SQLException { + String col1Value = "2015-05-05"; + String col2Value = "126.1230"; + String destTableName = "dest_sqlVariant"; + Utils.dropTableIfExists(tableName, stmt); + Utils.dropTableIfExists(destTableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant, col2 sql_variant)"); + stmt.executeUpdate("INSERT into " + tableName + "(col1, col2) values (CAST ('" + col1Value + "' AS " + "date" + ")" + ",CAST (" + col2Value + + " AS " + "smallmoney" + ") )"); + stmt.executeUpdate("create table " + destTableName + " (col1 sql_variant, col2 sql_variant)"); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals("" + rs.getDate(1), col1Value); + assertEquals(rs.getSmallMoney(2), new BigDecimal(col2Value)); + } + + } + + /** + * test time with scale value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestTimeWithScale() throws SQLException { + String col1Value = "'12:26:27.1452367'"; + beforeEachSetup("time(2)", col1Value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals("" + rs.getString(1), "12:26:27.15"); // getTime does not work + } + + } + + /** + * test char value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestChar() throws SQLException { + String col1Value = "'sample'"; + + beforeEachSetup("char", col1Value); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals("'" + rs.getString(1).trim() + "'", col1Value); // adds space between + } + + } + + /** + * test nchar value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestNchar() throws SQLException { + String col1Value = "'a'"; + + beforeEachSetup("nchar", col1Value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals("'" + rs.getNString(1).trim() + "'", col1Value); + } + + } + + /** + * test varchar value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestVarchar() throws SQLException { + String col1Value = "'hello'"; + + beforeEachSetup("varchar", col1Value); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals("'" + rs.getString(1).trim() + "'", col1Value); + } + + } + + /** + * test nvarchar value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestNvarchar() throws SQLException { + String col1Value = "'hello'"; + beforeEachSetup("nvarchar", col1Value); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals("'" + rs.getString(1).trim() + "'", col1Value); + } + + } + + /** + * test Binary value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestBinary20() throws SQLException { + String col1Value = "hello"; + beforeEachSetup("binary(20)", "'" + col1Value + "'"); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertTrue(Utils.parseByte(rs.getBytes(1), col1Value.getBytes())); + } + } + + /** + * test varbinary value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestVarbinary20() throws SQLException { + String col1Value = "hello"; + + beforeEachSetup("varbinary(20)", "'" + col1Value + "'"); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertTrue(Utils.parseByte(rs.getBytes(1), col1Value.getBytes())); + } + } + + /** + * test varbinary8000 + * + * @throws SQLException + */ + @Test + public void bulkCopyTestVarbinary8000() throws SQLException { + String col1Value = "hello"; + beforeEachSetup("binary(8000)", "'" + col1Value + "'"); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertTrue(Utils.parseByte(rs.getBytes(1), col1Value.getBytes())); + } + } + + /** + * test null value for underlying bit data type + * + * @throws SQLException + */ + @Test // TODO: check bitnull + public void bulkCopyTestBitNull() throws SQLException { + beforeEachSetup("bit", null); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals(rs.getBoolean(1), false); + } + } + + /** + * test bit value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestBit() throws SQLException { + int col1Value = 5000; + beforeEachSetup("bit", col1Value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals(rs.getBoolean(1), true); + } + } + + /** + * test datetime value + * + * @throws SQLException + */ + @Test + public void bulkCopyTestDatetime() throws SQLException { + String col1Value = "2015-05-08 12:26:24.0"; + beforeEachSetup("datetime", "'" + col1Value + "'"); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals("" + rs.getDateTime(1), col1Value); + + } + + } + + /** + * test smalldatetime + * + * @throws SQLException + */ + @Test + public void bulkCopyTestSmalldatetime() throws SQLException { + String col1Value = "2015-05-08 12:26:24"; + beforeEachSetup("smalldatetime", "'" + col1Value + "'"); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals("" + rs.getSmallDateTime(1), "2015-05-08 12:26:00.0"); + } + + } + + /** + * test datetime2 + * + * @throws SQLException + */ + @Test + public void bulkCopyTestDatetime2() throws SQLException { + String col1Value = "2015-05-08 12:26:24.12645"; + beforeEachSetup("datetime2(2)", "'" + col1Value + "'"); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals("" + rs.getTimestamp(1), "2015-05-08 12:26:24.13"); + } + + } + + /** + * test time + * + * @throws SQLException + */ + @Test + public void bulkCopyTestTime() throws SQLException { + String col1Value = "'12:26:27.1452367'"; + String destTableName = "dest_sqlVariant"; + Utils.dropTableIfExists(tableName, stmt); + Utils.dropTableIfExists(destTableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant)"); + stmt.executeUpdate("INSERT into " + tableName + "(col1) values (CAST (" + col1Value + " AS " + "time(2)" + ") )"); + stmt.executeUpdate("create table " + destTableName + " (col1 sql_variant)"); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + rs.next(); + assertEquals("" + rs.getObject(1).toString(), "12:26:27.15"); // TODO + } + + /** + * Read GUID stored in SqlVariant + * + * @throws SQLException + */ + @Test + public void bulkCopyTestReadGUID() throws SQLException { + String col1Value = "1AE740A2-2272-4B0F-8086-3DDAC595BC11"; + beforeEachSetup("uniqueidentifier", "'" + col1Value + "'"); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals("" + rs.getUniqueIdentifier(1), col1Value); + + } + } + + /** + * Read VarChar8000 from SqlVariant + * + * @throws SQLException + */ + @Test + public void bulkCopyTestVarChar8000() throws SQLException { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < 8000; i++) { + buffer.append("a"); + } + String col1Value = buffer.toString(); + beforeEachSetup("varchar(8000)", "'" + col1Value + "'"); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(con); + bulkCopy.setDestinationTableName(destTableName); + bulkCopy.writeToServer(rs); + bulkCopy.close(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTableName); + while (rs.next()) { + assertEquals(rs.getString(1), col1Value); + } + } + + private void beforeEachSetup(String colType, + Object colValue) throws SQLException { + Utils.dropTableIfExists(tableName, stmt); + Utils.dropTableIfExists(destTableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant)"); + stmt.executeUpdate("INSERT into " + tableName + "(col1) values (CAST (" + colValue + " AS " + colType + ") )"); + stmt.executeUpdate("create table " + destTableName + " (col1 sql_variant)"); + } + + /** + * Prepare test + * + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ + @BeforeAll + public static void setupHere() throws SQLException, SecurityException, IOException { + con = (SQLServerConnection) DriverManager.getConnection(connectionString); + stmt = con.createStatement(); + } + + /** + * drop the tables + * + * @throws SQLException + */ + @AfterAll + public static void afterAll() throws SQLException { + Utils.dropTableIfExists(tableName, stmt); + Utils.dropTableIfExists(destTableName, stmt); + + if (null != stmt) { + stmt.close(); + } + + if (null != rs) { + rs.close(); + } + + if (null != con) { + con.close(); + } + } +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/RandomData.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/RandomData.java new file mode 100644 index 0000000000..074dbe9c1a --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/RandomData.java @@ -0,0 +1,651 @@ +package com.microsoft.sqlserver.jdbc.datatypes; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.Random; + +import microsoft.sql.DateTimeOffset; + +public class RandomData { + + private static Random r = new Random(); + + public static boolean returnNull = (0 == r.nextInt(5)); //20% chance of return null + public static boolean returnFullLength = (0 == r.nextInt(2)); //50% chance of return full length for char/nchar and binary types + public static boolean returnMinMax = (0 == r.nextInt(5)); //20% chance of return Min/Max value + public static boolean returnZero = (0 == r.nextInt(10)); //10% chance of return zero + + private static String specicalCharSet = "ÀÂÃÄËßîðÐ"; + private static String normalCharSet = "1234567890-=!@#$%^&*()_+qwertyuiop[]\\asdfghjkl;'zxcvbnm,./QWERTYUIOP{}|ASDFGHJKL:\"ZXCVBNM<>?"; + + private static String unicodeCharSet = "♠♣♥♦林花謝了春紅太匆匆無奈朝我附件为放假哇额外放我放问역사적으로본래한민족의영역은만주와연해주의일부를포함하였으나会和太空特工我來寒雨晚來風胭脂淚留人醉幾時重自是人生長恨水長東ྱོགས་སུ་འཁོར་བའི་ས་ཟླུུམ་ཞིག་ལ་ངོས་འཛིན་དགོས་ཏེ།ངག་ཕྱོαβγδεζηθικλμνξοπρστυφχψ太陽系の年齢もまた隕石の年代測定に依拠するので"; + private static String numberCharSet = "1234567890"; + private static String numberCharSet2 = "123456789"; + + //-------------------numeric types-------------------------------------------- + + public static Boolean generateBoolean(boolean nullable){ + if(nullable){ + if(returnNull){ + return null; + } + } + + return r.nextBoolean(); + } + + public static Integer generateInt(boolean nullable){ + if(nullable){ + if(returnNull){ + return null; + } + } + + if(returnZero){ + return 0; + } + + if(returnMinMax){ + if(r.nextBoolean()){ + return 2147483647; + } + else{ + return -2147483648; + } + } + + //can be either negative or positive + return r.nextInt(); + } + + public static Long generateLong(boolean nullable){ + if(nullable){ + if(returnNull){ + return null; + } + } + + if(returnZero){ + return 0L; + } + + if(returnMinMax){ + if(r.nextBoolean()){ + return 9223372036854775807L; + } + else{ + return -9223372036854775808L; + } + } + + //can be either negative or positive + return r.nextLong(); + } + + public static Short generateTinyint(boolean nullable){ + Integer value = pickInt(nullable, 255, 0); + + if(null != value){ + return value.shortValue(); + } + else{ + return null; + } + } + + public static Short generateSmallint(boolean nullable){ + Integer value = pickInt(nullable, 32767, -32768); + + if(null != value){ + return value.shortValue(); + } + else{ + return null; + } + } + + private static Integer pickInt(boolean nullable, int max, int min){ + if(nullable){ + if(returnNull){ + return null; + } + } + + if(returnZero){ + return 0; + } + + if(returnMinMax){ + if(r.nextBoolean()){ + return max; + } + else{ + return min; + } + } + + return (int) r.nextInt(max - min) + min; + } + + public static BigDecimal generateDecimalNumeric(int precision, int scale, boolean nullable){ + + if(nullable){ + if(returnNull){ + return null; + } + } + + if(returnZero){ + return BigDecimal.ZERO.setScale(scale); + + } + + if(returnMinMax){ + BigInteger n ; + if(r.nextBoolean()){ + n = BigInteger.TEN.pow(precision); + if(scale>0) + return new BigDecimal(n, scale).subtract(new BigDecimal(""+Math.pow(10, -scale)).setScale(scale, BigDecimal.ROUND_HALF_UP)).negate(); + else + return new BigDecimal(n, scale).subtract(new BigDecimal("1")).negate(); + } + else{ + n = BigInteger.TEN.pow(precision); + if(scale>0) + return new BigDecimal(n, scale).subtract(new BigDecimal(""+Math.pow(10, -scale)).setScale(scale, BigDecimal.ROUND_HALF_UP)).negate(); + else + return new BigDecimal(n, scale).subtract(new BigDecimal("1")).negate(); + + } + + } + BigInteger n = BigInteger.TEN.pow(precision); + if(r.nextBoolean()) { + return new BigDecimal(newRandomBigInteger(n, r, precision), scale); + } + return (new BigDecimal(newRandomBigInteger(n, r, precision), scale).negate()); + + } + private static BigInteger newRandomBigInteger(BigInteger n, Random rnd, int precision) { + BigInteger r; + do { + r = new BigInteger(n.bitLength(), rnd); + } while (r.toString().length() != precision); + + return r; + } + + public static Float generateReal(boolean nullable){ + Double doubleValue = generateFloat(24, nullable); + + if(null != doubleValue){ + return doubleValue.floatValue(); + } + else{ + return null; + } + } + + public static Double generateFloat(Integer n, boolean nullable){ + if(nullable){ + if(returnNull){ + return null; + } + } + + if(returnZero){ + return new Double(0); + } + + //only 2 options: 24 or 53 + //The default value of n is 53. If 1<=n<=24, n is treated as 24. If 25<=n<=53, n is treated as 53. + //https://msdn.microsoft.com/en-us/library/ms173773.aspx + if(null == n){ + n = 53; + } + else if(25 <= n && 53 >= n){ + n = 53; + } + else{ + n = 24; + } + + if(returnMinMax){ + if(53 == n){ + if(r.nextBoolean()){ + if(r.nextBoolean()){ + return Double.valueOf("1.79E+308"); + } + else{ + return Double.valueOf("2.23E-308"); + } + } + else{ + if(r.nextBoolean()){ + return Double.valueOf("-2.23E-308"); + } + else{ + return Double.valueOf("-1.79E+308"); + } + } + } + else{ + if(r.nextBoolean()){ + if(r.nextBoolean()){ + return Double.valueOf("3.40E+38"); + } + else{ + return Double.valueOf("1.18E-38"); + } + } + else{ + if(r.nextBoolean()){ + return Double.valueOf("-1.18E-38"); + } + else{ + return Double.valueOf("-3.40E+38"); + } + } + } + } + + String intPart = "" + r.nextInt(10); + + //generate n bits of binary data and convert to long, then use the long as decimal part + StringBuffer sb = new StringBuffer(); + for(int i = 0; i < n; i++){ + sb.append(r.nextInt(2)); + } + long longValue = Long.parseLong(sb.toString(), 2); + String stringValue = intPart + "." + longValue; + + return Double.valueOf(stringValue); + } + + public static BigDecimal generateMoney(boolean nullable){ + String charSet = numberCharSet; + BigDecimal max = new BigDecimal("922337203685477.5807"); + BigDecimal min = new BigDecimal("-922337203685477.5808"); + float multiplier = 10000; + return generateMoneyOrSmallMoney(nullable, max, min, multiplier, charSet); + } + + public static BigDecimal generateSmallMoney(boolean nullable){ + String charSet = numberCharSet; + BigDecimal max = new BigDecimal("214748.3647"); + BigDecimal min = new BigDecimal("-214748.3648"); + float multiplier = (float) (1.0/10000.0); + return generateMoneyOrSmallMoney(nullable, max, min, multiplier, charSet); + } + + private static BigDecimal generateMoneyOrSmallMoney(boolean nullable, BigDecimal max, BigDecimal min, float multiplier, String charSet){ + if(nullable){ + if(returnNull){ + return null; + } + } + + if(returnZero){ + return BigDecimal.ZERO.setScale(4); + } + + if(returnMinMax){ + if(r.nextBoolean()){ + return max; + } + else{ + return min; + } + } + + long intPart = (long)(r.nextInt() * multiplier); + + StringBuffer sb = new StringBuffer(); + for(int i = 0; i < 4; i++){ + char c = pickRandomChar(charSet); + sb.append(c); + } + + return new BigDecimal(intPart + "." + sb.toString()); + } + + + //----------------Char NChar types------------------------------------------ + + public static String generateCharTypes(String columnLength, boolean nullable, boolean encrypted){ + String charSet = normalCharSet; + + return buildCharOrNChar(columnLength, nullable, encrypted, charSet, 8001); + } + + public static String generateNCharTypes(String columnLength, boolean nullable, boolean encrypted){ + String charSet = specicalCharSet + normalCharSet + unicodeCharSet; + + return buildCharOrNChar(columnLength, nullable, encrypted, charSet, 4001); + } + + private static String buildCharOrNChar(String columnLength, boolean nullable, boolean encrypted, String charSet, int maxBound){ + + if(nullable){ + if(returnNull){ + return null; + } + } + + //if column is encrypted, string value cannot be "", not supported. + int minimumLength = 0; + if(encrypted){ + minimumLength = 1; + } + + int length; + if(columnLength.toLowerCase().equals("max")){ + //50% chance of return value longer than 8000/4000 + if(r.nextBoolean()){ + length = r.nextInt(100000) + maxBound; + return buildRandomString(length, charSet); + } + else{ + length = r.nextInt(maxBound - minimumLength) + minimumLength; + return buildRandomString(length, charSet); + } + } + else{ + int columnLengthInt = Integer.parseInt(columnLength); + if(returnFullLength){ + length = columnLengthInt; + return buildRandomString(length, charSet); + } + else{ + length = r.nextInt(columnLengthInt - minimumLength) + minimumLength; + return buildRandomString(length, charSet); + } + } + } + + private static String buildRandomString(int length, String charSet){ + StringBuffer sb = new StringBuffer(); + for(int i = 0; i < length; i++){ + char c = pickRandomChar(charSet); + sb.append(c); + } + + return sb.toString(); + } + + private static char pickRandomChar(String charSet){ + int charSetLength = charSet.length(); + + int randomIndex = r.nextInt(charSetLength); + return charSet.charAt(randomIndex); + } + + + //-----------------------Binary types-------------------------- + + public static byte[] generateBinaryTypes(String columnLength, boolean nullable, boolean encrypted){ + int maxBound = 8001; + + if(nullable){ + if(returnNull){ + return null; + } + } + + //if column is encrypted, string value cannot be "", not supported. + int minimumLength = 0; + if(encrypted){ + minimumLength = 1; + } + + int length; + if(columnLength.toLowerCase().equals("max")){ + //50% chance of return value longer than 8000/4000 + if(r.nextBoolean()){ + length = r.nextInt(100000) + maxBound; + byte[] bytes = new byte[length]; + r.nextBytes(bytes); + return bytes; + } + else{ + length = r.nextInt(maxBound - minimumLength) + minimumLength; + byte[] bytes = new byte[length]; + r.nextBytes(bytes); + return bytes; + } + } + else{ + int columnLengthInt = Integer.parseInt(columnLength); + if(returnFullLength){ + length = columnLengthInt; + byte[] bytes = new byte[length]; + r.nextBytes(bytes); + return bytes; + } + else{ + length = r.nextInt(columnLengthInt - minimumLength) + minimumLength; + byte[] bytes = new byte[length]; + r.nextBytes(bytes); + return bytes; + } + } + } + + + + //-----------------------Temporal types-------------------------- + + public static Date generateDate(boolean nullable){ + if(nullable){ + if(returnNull){ + return null; + } + } + + long max = Timestamp.valueOf("9999-12-31 00:00:00.000").getTime(); + long min = Timestamp.valueOf("0001-01-01 00:00:00.000").getTime(); + + if(returnMinMax){ + if(r.nextBoolean()){ + return new Date(max); + } + else{ + return new Date(min); + } + } + + while(true){ + long longValue = r.nextLong(); + + if(longValue >= min && longValue <= max){ + return new Date(longValue); + } + } + } + + public static Timestamp generateDatetime(boolean nullable){ + long max = Timestamp.valueOf("9999-12-31 23:59:59.997").getTime(); + long min = Timestamp.valueOf("1753-01-01 00:00:00.000").getTime(); + + return generateTimestamp(nullable, max, min); + } + + public static Timestamp generateSmalldatetime(boolean nullable){ + long max = Timestamp.valueOf("2079-06-06 23:59:00").getTime(); + long min = Timestamp.valueOf("1900-01-01 00:00:00").getTime(); + + return generateTimestamp(nullable, max, min); + } + + public static Timestamp generateDatetime2(Integer precision, boolean nullable){ + if(null == precision){ + precision = 7; + } + + long max = Timestamp.valueOf("9999-12-31 23:59:59").getTime(); + long min = Timestamp.valueOf("0001-01-01 00:00:00").getTime(); + + Timestamp ts = generateTimestamp(nullable, max, min); + + if(null == ts){ + return ts; + } + + if(returnMinMax){ + if(ts.getTime() == max){ + int precisionDigits = buildPrecision(precision, "9"); + ts.setNanos(precisionDigits); + return ts; + } + else{ + ts.setNanos(0); + return ts; + } + } + + int precisionDigits = buildPrecision(precision, numberCharSet2); //not to use 0 in the random data for now. E.g creates 9040330 and when set it is 904033. + ts.setNanos(precisionDigits); + return ts; + } + + public static Time generateTime(Integer precision, boolean nullable){ + if(null == precision){ + precision = 7; + } + + long max = Timestamp.valueOf("9999-12-31 23:59:59").getTime(); + long min = Timestamp.valueOf("0001-01-01 00:00:00").getTime(); + + Timestamp ts = generateTimestamp(nullable, max, min); + + if(null == ts){ + return null; + } + + if(returnMinMax){ + if(ts.getTime() == max){ + int precisionDigits = buildPrecision(precision, "9"); + ts.setNanos(precisionDigits); + return new Time(ts.getTime()); + } + else{ + ts.setNanos(0); + return new Time(ts.getTime()); + } + } + + int precisionDigits = buildPrecision(precision, numberCharSet); + ts.setNanos(precisionDigits); + return new Time(ts.getTime()); + } + + private static int buildPrecision(int precision, String charSet){ + String stringValue = calculatePrecisionDigits(precision, charSet); + return Integer.parseInt(stringValue); + } + + //setNanos(999999900) gives 00:00:00.9999999 + //so, this value has to be 9 digits + private static String calculatePrecisionDigits(int precision, String charSet){ + StringBuffer sb = new StringBuffer(); + for(int i = 0; i < precision; i++){ + char c = pickRandomChar(charSet); + sb.append(c); + } + + for(int i = sb.length(); i < 9; i++){ + sb.append("0"); + } + + return sb.toString(); + } + + private static Timestamp generateTimestamp(boolean nullable, long max, long min){ + if(nullable){ + if(returnNull){ + return null; + } + } + + if(returnMinMax){ + if(r.nextBoolean()){ + return new Timestamp(max); + } + else{ + return new Timestamp(min); + } + } + + while(true){ + long longValue = r.nextLong(); + + if(longValue >= min && longValue <= max){ + return new Timestamp(longValue); + } + } + } + + public static DateTimeOffset generateDatetimeoffset(Integer precision, boolean nullable){ + if(null == precision){ + precision = 7; + } + + DateTimeOffset maxDTS = calculateDateTimeOffsetMinMax("max", precision, "9999-12-31 23:59:59"); + DateTimeOffset minDTS = calculateDateTimeOffsetMinMax("min", precision, "0001-01-01 00:00:00"); + + long max = maxDTS.getTimestamp().getTime(); + long min = minDTS.getTimestamp().getTime(); + + Timestamp ts = generateTimestamp(nullable, max, min); + + if(null == ts){ + return null; + } + + if(returnMinMax){ + if(r.nextBoolean()){ + return maxDTS; + } + else{ + //return minDTS; + return calculateDateTimeOffsetMinMax("min", precision, "0001-01-01 00:00:00.0000000"); + } + } + + int precisionDigits = buildPrecision(precision, numberCharSet2); + ts.setNanos(precisionDigits); + + int randomTimeZoneInMinutes = r.nextInt(1681) - 840; + + return microsoft.sql.DateTimeOffset.valueOf(ts, randomTimeZoneInMinutes); + } + + private static DateTimeOffset calculateDateTimeOffsetMinMax(String maxOrMin, Integer precision, String tsMinMax){ + int providedTimeZoneInMinutes; + if(maxOrMin.toLowerCase().equals("max")){ + providedTimeZoneInMinutes = 840; + } + else{ + providedTimeZoneInMinutes = -840; + } + + Timestamp tsMax = Timestamp.valueOf(tsMinMax); + + Calendar cal = Calendar.getInstance(); + long offset = cal.get(Calendar.ZONE_OFFSET); //in milliseconds + + //max Timestamp + difference of current time zone and GMT - provided time zone in milliseconds + tsMax = new Timestamp(tsMax.getTime() + offset - (providedTimeZoneInMinutes * 60 * 1000)); + + if(maxOrMin.toLowerCase().equals("max")){ + int precisionDigits = buildPrecision(precision, "9"); + tsMax.setNanos(precisionDigits); + } + + return microsoft.sql.DateTimeOffset.valueOf(tsMax, providedTimeZoneInMinutes); + } +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantResultSetTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantResultSetTest.java new file mode 100644 index 0000000000..fe5a6c3397 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantResultSetTest.java @@ -0,0 +1,869 @@ +/* + * 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.datatypes; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.math.BigDecimal; +import java.sql.CallableStatement; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Arrays; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.SQLServerException; +import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; +import com.microsoft.sqlserver.jdbc.SQLServerResultSet; +import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.Utils; + +/** + * Tests for supporting sqlVariant + * + */ +@RunWith(JUnitPlatform.class) +public class SQLVariantResultSetTest extends AbstractTest { + + static SQLServerConnection con = null; + static Statement stmt = null; + static String tableName = "sqlVariantTestSrcTable"; + static String inputProc = "sqlVariantProc"; + static SQLServerResultSet rs = null; + static SQLServerPreparedStatement pstmt = null; + + /** + * Read int value + * + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ + @Test + public void readInt() throws SQLException, SecurityException, IOException { + int value = 2; + createAndPopulateTable("int", value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getString(1), "" + value); + } + + /** + * Read money type stored in SqlVariant + * + * @throws SQLException + * + */ + @Test + public void readMoney() throws SQLException { + Double value = 123.12; + createAndPopulateTable("Money", value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getObject(1), new BigDecimal("123.1200")); + } + + /** + * Reading smallmoney from SqlVariant + * + * @throws SQLException + */ + @Test + public void readSmallMoney() throws SQLException { + Double value = 123.12; + createAndPopulateTable("smallmoney", value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getObject(1), new BigDecimal("123.1200")); + } + + /** + * Read GUID stored in SqlVariant + * + * @throws SQLException + */ + @Test + public void readGUID() throws SQLException { + String value = "1AE740A2-2272-4B0F-8086-3DDAC595BC11"; + createAndPopulateTable("uniqueidentifier", "'" + value + "'"); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getUniqueIdentifier(1), value); + } + + /** + * Reading date stored in SqlVariant + * + * @throws SQLException + */ + @Test + public void readDate() throws SQLException { + String value = "'2015-05-08'"; + createAndPopulateTable("date", value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals("" + rs.getObject(1), "2015-05-08"); + } + + /** + * Read time from SqlVariant + * + * @throws SQLException + */ + @Test + public void readTime() throws SQLException { + String value = "'12:26:27.123345'"; + createAndPopulateTable("time(3)", value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals("" + rs.getObject(1).toString(), "12:26:27.123"); // TODO + } + + /** + * Read datetime from SqlVariant + * + * @throws SQLException + */ + @Test + public void readDateTime() throws SQLException { + String value = "'2015-05-08 12:26:24'"; + createAndPopulateTable("datetime", value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals("" + rs.getObject(1), "2015-05-08 12:26:24.0"); + } + + /** + * Read smalldatetime from SqlVariant + * + * @throws SQLException + */ + @Test + public void readSmallDateTime() throws SQLException { + String value = "'2015-05-08 12:26:24'"; + createAndPopulateTable("smalldatetime", value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals("" + rs.getObject(1), "2015-05-08 12:26:00.0"); + } + + /** + * Read VarChar8000 from SqlVariant + * + * @throws SQLException + */ + @Test + public void readVarChar8000() throws SQLException { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < 8000; i++) { + buffer.append("a"); + } + String value = "'" + buffer.toString() + "'"; + createAndPopulateTable("VARCHAR(8000)", value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getObject(1), buffer.toString()); + } + + /** + * Read float from SqlVariant + * + * @throws SQLException + */ + @Test + public void readFloat() throws SQLException { + float value = 5; + createAndPopulateTable("float", value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getObject(1), Double.valueOf("5.0")); + } + + /** + * Read bigint from SqlVariant + * + * @throws SQLException + */ + @Test + public void readBigInt() throws SQLException { + long value = 5; + createAndPopulateTable("bigint", value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getObject(1), value); + } + + /** + * read smallint from SqlVariant + * + * @throws SQLException + */ + @Test + public void readSmallInt() throws SQLException { + short value = 5; + createAndPopulateTable("smallint", value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getObject(1), value); + } + + /** + * Read tinyint from SqlVariant + * + * @throws SQLException + */ + @Test + public void readTinyInt() throws SQLException { + short value = 5; + createAndPopulateTable("tinyint", value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getObject(1), value); + } + + /** + * read bit from SqlVariant + * + * @throws SQLException + */ + @Test + public void readBit() throws SQLException { + int value = 50000; + createAndPopulateTable("bit", value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getObject(1), true); + } + + /** + * Read float from SqlVariant + * + * @throws SQLException + */ + @Test + public void readReal() throws SQLException { + float value = 5; + createAndPopulateTable("Real", value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getObject(1), Float.valueOf("5.0")); + } + + /** + * Read nchar from SqlVariant + * + * @throws SQLException + */ + @Test + public void readNChar() throws SQLException, SecurityException, IOException { + String value = "a"; + createAndPopulateTable("nchar(5)", "'" + value + "'"); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getNString(1).trim(), value); + } + + /** + * Read nVarChar + * + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ + @Test + public void readNVarChar() throws SQLException, SecurityException, IOException { + String value = "nvarchar"; + createAndPopulateTable("nvarchar(10)", "'" + value + "'"); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getObject(1), value); + } + + /** + * readBinary + * + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ + @Test + public void readBinary20() throws SQLException, SecurityException, IOException { + String value = "hi"; + createAndPopulateTable("binary(20)", "'" + value + "'"); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertTrue(parseByte((byte[]) rs.getObject(1), (byte[]) value.getBytes())); + } + + /** + * read varBinary + * + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ + @Test + public void readVarBinary20() throws SQLException, SecurityException, IOException { + String value = "hi"; + createAndPopulateTable("varbinary(20)", "'" + value + "'"); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertTrue(parseByte((byte[]) rs.getObject(1), (byte[]) value.getBytes())); + } + + /** + * read Binary512 + * + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ + @Test + public void readBinary512() throws SQLException, SecurityException, IOException { + String value = "hi"; + createAndPopulateTable("binary(512)", "'" + value + "'"); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertTrue(parseByte((byte[]) rs.getObject(1), (byte[]) value.getBytes())); + } + + /** + * read Binary(8000) + * + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ + @Test + public void readBinary8000() throws SQLException, SecurityException, IOException { + String value = "hi"; + createAndPopulateTable("binary(8000)", "'" + value + "'"); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertTrue(parseByte((byte[]) rs.getObject(1), (byte[]) value.getBytes())); + } + + /** + * read varBinary(8000) + * + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ + @Test + public void readvarBinary8000() throws SQLException, SecurityException, IOException { + String value = "hi"; + createAndPopulateTable("varbinary(8000)", "'" + value + "'"); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertTrue(parseByte((byte[]) rs.getObject(1), (byte[]) value.getBytes())); + } + + /** + * Read SqlVariantProperty + * + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ + @Test + public void readSQLVariantProperty() throws SQLException, SecurityException, IOException { + String value = "hi"; + createAndPopulateTable("binary(8000)", "'" + value + "'"); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT SQL_VARIANT_PROPERTY(col1,'BaseType') AS 'Base Type'," + + " SQL_VARIANT_PROPERTY(col1,'Precision') AS 'Precision' from " + tableName); + rs.next(); + assertTrue(rs.getString(1).equalsIgnoreCase("binary"), "unexpected baseType, expected: binary, retrieved:" + rs.getString(1)); + } + + /** + * Testing that inserting value more than 8000 on varchar(8000) should throw failure operand type clash + * + * @throws SQLException + */ + @Test + public void insertVarChar8001() throws SQLException { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < 8001; i++) { + buffer.append("a"); + } + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant)"); + SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con.prepareStatement("insert into " + tableName + " values (?)"); + pstmt.setObject(1, buffer.toString()); + try { + pstmt.execute(); + } + catch (SQLServerException e) { + assertTrue(e.toString().contains("com.microsoft.sqlserver.jdbc.SQLServerException: Operand type clash")); + } + } + + /** + * Testing nvarchar4000 + * + * @throws SQLException + */ + @Test + public void readNvarChar4000() throws SQLException { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < 4000; i++) { + buffer.append("a"); + } + String value = "'" + buffer.toString() + "'"; + createAndPopulateTable("NVARCHAR(4000)", value); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getObject(1), buffer.toString()); + } + + /** + * Update int value + * + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ + @Test + public void UpdateInt() throws SQLException, SecurityException, IOException { + int value = 2; + int updatedValue = 3; + createAndPopulateTable("int", value); + stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getString(1), "" + value); + rs.updateInt(1, updatedValue); + rs.updateRow(); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getString(1), "" + updatedValue); + if (null != rs) { + rs.close(); + } + } + + /** + * Update nChar value + * + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ + @Test + public void UpdateNChar() throws SQLException, SecurityException, IOException { + String value = "a"; + String updatedValue = "b"; + + createAndPopulateTable("nchar", "'" + value + "'"); + stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getString(1).trim(), "" + value); + rs.updateNString(1, updatedValue); + rs.updateRow(); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getString(1), "" + updatedValue); + if (null != rs) { + rs.close(); + } + } + + /** + * update Binary + * + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ + @Test + public void updateBinary20() throws SQLException, SecurityException, IOException { + String value = "hi"; + String updatedValue = "bye"; + createAndPopulateTable("binary(20)", "'" + value + "'"); + stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertTrue(parseByte((byte[]) rs.getObject(1), (byte[]) value.getBytes())); + rs.updateBytes(1, updatedValue.getBytes()); + rs.updateRow(); + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertTrue(parseByte((byte[]) rs.getBytes(1), updatedValue.getBytes())); + if (null != rs) { + rs.close(); + } + } + + /** + * Testing inserting and reading from SqlVariant and int column + * + * @throws SQLException + */ + @Test + public void insertTest() throws SQLException { + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant, col2 int)"); + SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con.prepareStatement("insert into " + tableName + " values (?, ?)"); + + String[] col1Value = {"Hello", null}; + int[] col2Value = {1, 2}; + pstmt.setObject(1, "Hello"); + pstmt.setInt(2, 1); + pstmt.execute(); + pstmt.setObject(1, null); + pstmt.setInt(2, 2); + pstmt.execute(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + int i = 0; + rs.next(); + do { + assertEquals(rs.getObject(1), col1Value[i]); + assertEquals(rs.getObject(2), col2Value[i]); + i++; + } + while (rs.next()); + } + + /** + * test inserting null value + * + * @throws SQLException + */ + @Test + public void insertTestNull() throws SQLException { + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant)"); + pstmt = (SQLServerPreparedStatement) con.prepareStatement("insert into " + tableName + " values ( ?)"); + + pstmt.setObject(1, null); + pstmt.execute(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getBoolean(1), false); + } + + /** + * Test inserting using setObject + * + * @throws SQLException + * @throws ParseException + */ + @Test + public void insertSetObject() throws SQLException { + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant)"); + pstmt = (SQLServerPreparedStatement) con.prepareStatement("insert into " + tableName + " values (?)"); + + pstmt.setObject(1, 2); + pstmt.execute(); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getObject(1), 2); + } + + /** + * Test callableStatement with SqlVariant + * + * @throws SQLException + */ + @Test + public void callableStatementOutputIntTest() throws SQLException { + int value = 5; + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant)"); + stmt.executeUpdate("INSERT into " + tableName + " values (CAST (" + value + " AS " + "int" + "))"); + + Utils.dropProcedureIfExists(inputProc, stmt); + String sql = "CREATE PROCEDURE " + inputProc + " @p0 sql_variant OUTPUT AS SELECT TOP 1 @p0=col1 FROM " + tableName; + stmt.execute(sql); + + CallableStatement cs = con.prepareCall(" {call " + inputProc + " (?) }"); + cs.registerOutParameter(1, microsoft.sql.Types.SQL_VARIANT); + cs.execute(); + assertEquals(cs.getString(1), String.valueOf(value)); + if (null != cs) { + cs.close(); + } + } + + /** + * Test callableStatement with SqlVariant + * + * @throws SQLException + */ + @Test + public void callableStatementOutputDateTest() throws SQLException { + String value = "2015-05-08"; + + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant)"); + stmt.executeUpdate("INSERT into " + tableName + " values (CAST ('" + value + "' AS " + "date" + "))"); + + Utils.dropProcedureIfExists(inputProc, stmt); + String sql = "CREATE PROCEDURE " + inputProc + " @p0 sql_variant OUTPUT AS SELECT TOP 1 @p0=col1 FROM " + tableName; + stmt.execute(sql); + + CallableStatement cs = con.prepareCall(" {call " + inputProc + " (?) }"); + cs.registerOutParameter(1, microsoft.sql.Types.SQL_VARIANT); + cs.execute(); + assertEquals(cs.getString(1), String.valueOf(value)); + if (null != cs) { + cs.close(); + } + } + + /** + * Test callableStatement with SqlVariant + * + * @throws SQLException + */ + @Test + public void callableStatementOutputTimeTest() throws SQLException { + String value = "12:26:27.123345"; + String returnValue = "12:26:27.123"; + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant)"); + stmt.executeUpdate("INSERT into " + tableName + " values (CAST ('" + value + "' AS " + "time(3)" + "))"); + + Utils.dropProcedureIfExists(inputProc, stmt); + String sql = "CREATE PROCEDURE " + inputProc + " @p0 sql_variant OUTPUT AS SELECT TOP 1 @p0=col1 FROM " + tableName; + stmt.execute(sql); + + CallableStatement cs = con.prepareCall(" {call " + inputProc + " (?) }"); + cs.registerOutParameter(1, microsoft.sql.Types.SQL_VARIANT, 3); + cs.execute(); + assertEquals(cs.getString(1), String.valueOf(returnValue)); + if (null != cs) { + cs.close(); + } + } + + /** + * Test callableStatement with SqlVariant Binary value + * + * @throws SQLException + */ + @Test + public void callableStatementOutputBinaryTest() throws SQLException { + byte[] binary20 = RandomData.generateBinaryTypes("20", false, false); + byte[] secondBinary20 = RandomData.generateBinaryTypes("20", false, false); + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant, col2 sql_variant)"); + pstmt = (SQLServerPreparedStatement) con.prepareStatement("insert into " + tableName + " values (?,?)"); + pstmt.setObject(1, binary20); + pstmt.setObject(2, secondBinary20); + pstmt.execute(); + Utils.dropProcedureIfExists(inputProc, stmt); + String sql = "CREATE PROCEDURE " + inputProc + " @p0 sql_variant OUTPUT, @p1 sql_variant" + " AS" + " SELECT top 1 @p0=col1 FROM " + tableName + + " where col2=@p1 "; + stmt.execute(sql); + + CallableStatement cs = con.prepareCall(" {call " + inputProc + " (?,?) }"); + cs.registerOutParameter(1, microsoft.sql.Types.SQL_VARIANT); + cs.setObject(2, secondBinary20, microsoft.sql.Types.SQL_VARIANT); + + cs.execute(); + assertTrue(parseByte((byte[]) cs.getBytes(1), binary20)); + if (null != cs) { + cs.close(); + } + } + + /** + * Test stored procedure with input and output params + * + * @throws SQLException + */ + @Test + public void callableStatementInputOutputIntTest() throws SQLException { + int col1Value = 5; + int col2Value = 2; + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant, col2 int)"); + stmt.executeUpdate("INSERT into " + tableName + "(col1, col2) values (CAST (" + col1Value + " AS " + "int" + "), " + col2Value + ")"); + Utils.dropProcedureIfExists(inputProc, stmt); + String sql = "CREATE PROCEDURE " + inputProc + " @p0 sql_variant OUTPUT, @p1 sql_variant" + " AS" + " SELECT top 1 @p0=col1 FROM " + tableName + + " where col2=@p1"; + stmt.execute(sql); + CallableStatement cs = con.prepareCall(" {call " + inputProc + " (?,?) }"); + + cs.registerOutParameter(1, microsoft.sql.Types.SQL_VARIANT); + cs.setObject(2, col2Value, microsoft.sql.Types.SQL_VARIANT); + cs.execute(); + assertEquals(cs.getObject(1), col1Value); + if (null != cs) { + cs.close(); + } + } + + /** + * Test stored procedure with input and output and return value + * + * @throws SQLException + */ + @Test + public void callableStatementInputOutputReturnIntTest() throws SQLException { + int col1Value = 5; + int col2Value = 2; + int returnValue = 12; + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant, col2 int)"); + stmt.executeUpdate("INSERT into " + tableName + "(col1, col2) values (CAST (" + col1Value + " AS " + "int" + "), " + col2Value + ")"); + Utils.dropProcedureIfExists(inputProc, stmt); + String sql = "CREATE PROCEDURE " + inputProc + " @p0 sql_variant OUTPUT, @p1 sql_variant" + " AS" + " SELECT top 1 @p0=col1 FROM " + tableName + + " where col2=@p1" + " return " + returnValue; + stmt.execute(sql); + CallableStatement cs = con.prepareCall(" {? = call " + inputProc + " (?,?) }"); + + cs.registerOutParameter(1, microsoft.sql.Types.SQL_VARIANT); + cs.registerOutParameter(2, microsoft.sql.Types.SQL_VARIANT); + cs.setObject(3, col2Value, microsoft.sql.Types.SQL_VARIANT); + cs.execute(); + assertEquals(cs.getString(1), String.valueOf(returnValue)); + assertEquals(cs.getString(2), String.valueOf(col1Value)); + if (null != cs) { + cs.close(); + } + } + + /** + * test input output procedure + * + * @throws SQLException + */ + @Test + public void callableStatementInputOutputReturnStringTest() throws SQLException { + String col1Value = "aa"; + String col2Value = "bb"; + int returnValue = 12; + + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant, col2 sql_variant)"); + stmt.executeUpdate("INSERT into " + tableName + "(col1,col2) values" + " (CAST ('" + col1Value + "' AS " + "varchar(5)" + ")" + " ,CAST ('" + + col2Value + "' AS " + "varchar(5)" + ")" + ")"); + Utils.dropProcedureIfExists(inputProc, stmt); + String sql = "CREATE PROCEDURE " + inputProc + " @p0 sql_variant OUTPUT, @p1 sql_variant" + " AS" + " SELECT top 1 @p0=col1 FROM " + tableName + + " where col2=@p1 " + " return " + returnValue; + stmt.execute(sql); + CallableStatement cs = con.prepareCall(" {? = call " + inputProc + " (?,?) }"); + cs.registerOutParameter(1, java.sql.Types.INTEGER); + cs.registerOutParameter(2, microsoft.sql.Types.SQL_VARIANT); + cs.setObject(3, col2Value, microsoft.sql.Types.SQL_VARIANT); + + cs.execute(); + assertEquals(returnValue, cs.getObject(1)); + assertEquals(cs.getObject(2), col1Value); + if (null != cs) { + cs.close(); + } + } + + /** + * Read several rows from SqlVariant + * + * @throws SQLException + */ + @Test + public void readSeveralRows() throws SQLException { + short value1 = 5; + int value2 = 10; + String value3 = "hi"; + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant, col2 sql_variant, col3 sql_variant)"); + stmt.executeUpdate("INSERT into " + tableName + " values (CAST (" + value1 + " AS " + "tinyint" + ")" + ",CAST (" + value2 + " AS " + "int" + + ")" + ",CAST ('" + value3 + "' AS " + "char(2)" + ")" + ")"); + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + rs.next(); + assertEquals(rs.getObject(1), value1); + assertEquals(rs.getObject(2), value2); + assertEquals(rs.getObject(3), value3); + if (null != rs) { + rs.close(); + } + + } + + private boolean parseByte(byte[] expectedData, + byte[] retrieved) { + assertTrue(Arrays.equals(expectedData, Arrays.copyOf(retrieved, expectedData.length)), " unexpected BINARY value, expected"); + for (int i = expectedData.length; i < retrieved.length; i++) { + assertTrue(0 == retrieved[i], "unexpected data BINARY"); + } + return true; + } + + /** + * Create and populate table + * + * @param columnType + * @param value + * @throws SQLException + */ + private void createAndPopulateTable(String columnType, + Object value) throws SQLException { + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant)"); + stmt.executeUpdate("INSERT into " + tableName + " values (CAST (" + value + " AS " + columnType + "))"); + } + + /** + * Prepare test + * + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ + @BeforeAll + public static void setupHere() throws SQLException, SecurityException, IOException { + con = (SQLServerConnection) DriverManager.getConnection(connectionString); + stmt = con.createStatement(); + } + + /** + * drop the tables + * + * @throws SQLException + */ + @AfterAll + public static void afterAll() throws SQLException { + Utils.dropProcedureIfExists(inputProc, stmt); + Utils.dropTableIfExists(tableName, stmt); + + if (null != stmt) { + stmt.close(); + } + + if (null != pstmt) { + pstmt.close(); + } + + if (null != rs) { + rs.close(); + } + + if (null != con) { + con.close(); + } + } + +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/TVPWithSqlVariantTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/TVPWithSqlVariantTest.java new file mode 100644 index 0000000000..23e0d18de5 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/TVPWithSqlVariantTest.java @@ -0,0 +1,495 @@ +/* + * 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.datatypes; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.math.BigDecimal; +import java.sql.Date; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Random; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import com.microsoft.sqlserver.jdbc.SQLServerCallableStatement; +import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.SQLServerDataTable; +import com.microsoft.sqlserver.jdbc.SQLServerException; +import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; +import com.microsoft.sqlserver.jdbc.SQLServerResultSet; +import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.Utils; +import com.microsoft.sqlserver.testframework.sqlType.SqlDate; + +@RunWith(JUnitPlatform.class) +public class TVPWithSqlVariantTest extends AbstractTest { + + private static SQLServerConnection conn = null; + static SQLServerStatement stmt = null; + static SQLServerResultSet rs = null; + static SQLServerDataTable tvp = null; + private static String tvpName = "numericTVP"; + private static String destTable = "destTvpSqlVariantTable"; + private static String procedureName = "procedureThatCallsTVP"; + static SQLServerPreparedStatement pstmt = null; + + /** + * Test a previous failure regarding to numeric precision. Issue #211 + * + * @throws SQLServerException + */ + @Test + public void testInt() throws SQLServerException { + tvp = new SQLServerDataTable(); + tvp.addColumnMetadata("c1", microsoft.sql.Types.SQL_VARIANT); + tvp.addRow(12); + pstmt = (SQLServerPreparedStatement) connection.prepareStatement("INSERT INTO " + destTable + " select * from ? ;"); + pstmt.setStructured(1, tvpName, tvp); + pstmt.execute(); + if (null != pstmt) { + pstmt.close(); + } + + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTable); + while (rs.next()) { + assertEquals(rs.getInt(1), 12); + assertEquals(rs.getString(1), "" + 12); + assertEquals(rs.getObject(1), 12); + } + } + + /** + * Test with date value + * + * @throws SQLServerException + */ + @Test + public void testDate() throws SQLServerException { + SqlDate sqlDate = new SqlDate(); + Date date = (Date) sqlDate.createdata(); + tvp = new SQLServerDataTable(); + tvp.addColumnMetadata("c1", microsoft.sql.Types.SQL_VARIANT); + tvp.addRow(date); + pstmt = (SQLServerPreparedStatement) connection.prepareStatement("INSERT INTO " + destTable + " select * from ? ;"); + pstmt.setStructured(1, tvpName, tvp); + pstmt.execute(); + if (null != pstmt) { + pstmt.close(); + } + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTable); + while (rs.next()) { + assertEquals(rs.getString(1), "" + date); // TODO: GetDate has issues + } + } + + /** + * Test with money value + * + * @throws SQLServerException + */ + @Test + public void testMoney() throws SQLServerException { + tvp = new SQLServerDataTable(); + tvp.addColumnMetadata("c1", microsoft.sql.Types.SQL_VARIANT); + String[] numeric = createNumericValues(); + tvp.addRow(new BigDecimal(numeric[14])); + pstmt = (SQLServerPreparedStatement) connection.prepareStatement("INSERT INTO " + destTable + " select * from ? ;"); + pstmt.setStructured(1, tvpName, tvp); + pstmt.execute(); + if (null != pstmt) { + pstmt.close(); + } + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTable); + while (rs.next()) { + assertEquals(rs.getMoney(1), new BigDecimal(numeric[14])); + } + } + + /** + * Test with small int value + * + * @throws SQLServerException + */ + @Test + public void testSmallInt() throws SQLServerException { + tvp = new SQLServerDataTable(); + tvp.addColumnMetadata("c1", microsoft.sql.Types.SQL_VARIANT); + String[] numeric = createNumericValues(); + tvp.addRow(Short.valueOf(numeric[2])); + pstmt = (SQLServerPreparedStatement) connection.prepareStatement("INSERT INTO " + destTable + " select * from ? ;"); + pstmt.setStructured(1, tvpName, tvp); + pstmt.execute(); + + if (null != pstmt) { + pstmt.close(); + } + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTable); + while (rs.next()) { + assertEquals("" + rs.getInt(1), numeric[2]); + // System.out.println(rs.getShort(1)); //does not work says cannot cast integer to short cause it is written as int + } + } + + /** + * Test with bigint value + * + * @throws SQLServerException + */ + @Test + public void testBigInt() throws SQLServerException { + Random r = new Random(); + tvp = new SQLServerDataTable(); + tvp.addColumnMetadata("c1", microsoft.sql.Types.SQL_VARIANT); + String[] numeric = createNumericValues(); + tvp.addRow(Long.parseLong(numeric[4])); + + pstmt = (SQLServerPreparedStatement) connection.prepareStatement("INSERT INTO " + destTable + " select * from ? ;"); + pstmt.setStructured(1, tvpName, tvp); + pstmt.execute(); + if (null != pstmt) { + pstmt.close(); + } + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTable); + while (rs.next()) { + assertEquals(rs.getLong(1), Long.parseLong(numeric[4])); + } + } + + /** + * Test with boolean value + * + * @throws SQLServerException + */ + @Test + public void testBoolean() throws SQLServerException { + tvp = new SQLServerDataTable(); + tvp.addColumnMetadata("c1", microsoft.sql.Types.SQL_VARIANT); + String[] numeric = createNumericValues(); + tvp.addRow(Boolean.parseBoolean(numeric[0])); + pstmt = (SQLServerPreparedStatement) connection.prepareStatement("INSERT INTO " + destTable + " select * from ? ;"); + pstmt.setStructured(1, tvpName, tvp); + pstmt.execute(); + if (null != pstmt) { + pstmt.close(); + } + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTable); + while (rs.next()) { + assertEquals(rs.getBoolean(1), Boolean.parseBoolean(numeric[0])); + } + } + + /** + * Test with float value + * + * @throws SQLServerException + */ + @Test + public void testFloat() throws SQLServerException { + tvp = new SQLServerDataTable(); + tvp.addColumnMetadata("c1", microsoft.sql.Types.SQL_VARIANT); + String[] numeric = createNumericValues(); + tvp.addRow(Float.parseFloat(numeric[1])); + pstmt = (SQLServerPreparedStatement) connection.prepareStatement("INSERT INTO " + destTable + " select * from ? ;"); + pstmt.setStructured(1, tvpName, tvp); + pstmt.execute(); + if (null != pstmt) { + pstmt.close(); + } + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTable); + while (rs.next()) { + assertEquals(rs.getFloat(1), Float.parseFloat(numeric[1])); + } + } + + /** + * Test with nvarchar + * + * @throws SQLServerException + */ + @Test + public void testNvarChar() throws SQLServerException { + tvp = new SQLServerDataTable(); + tvp.addColumnMetadata("c1", microsoft.sql.Types.SQL_VARIANT); + String colValue = "س"; + tvp.addRow(colValue); + pstmt = (SQLServerPreparedStatement) connection.prepareStatement("INSERT INTO " + destTable + " select * from ? ;"); + pstmt.setStructured(1, tvpName, tvp); + pstmt.execute(); + if (null != pstmt) { + pstmt.close(); + } + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTable); + while (rs.next()) { + assertEquals(rs.getString(1), colValue); + } + } + + /** + * Test with varchar8000 + * + * @throws SQLServerException + */ + @Test + public void testVarChar8000() throws SQLServerException { + tvp = new SQLServerDataTable(); + tvp.addColumnMetadata("c1", microsoft.sql.Types.SQL_VARIANT); + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < 8000; i++) { + buffer.append("a"); + } + String value = buffer.toString(); + tvp.addRow(value); + + pstmt = (SQLServerPreparedStatement) connection.prepareStatement("INSERT INTO " + destTable + " select * from ? ;"); + pstmt.setStructured(1, tvpName, tvp); + pstmt.execute(); + if (null != pstmt) { + pstmt.close(); + } + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTable); + while (rs.next()) { + assertEquals(rs.getString(1), value); + } + } + + /** + * Check that we throw proper error message when inserting more than 8000 + * + * @throws SQLServerException + */ + @Test + public void testLongVarChar() throws SQLServerException { + tvp = new SQLServerDataTable(); + tvp.addColumnMetadata("c1", microsoft.sql.Types.SQL_VARIANT); + + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < 8001; i++) { + buffer.append("a"); + } + String value = buffer.toString(); + tvp.addRow(value); + + pstmt = (SQLServerPreparedStatement) connection.prepareStatement("INSERT INTO " + destTable + " select * from ? ;"); + pstmt.setStructured(1, tvpName, tvp); + try { + pstmt.execute(); + } + catch (SQLServerException e) { + assertTrue(e.getMessage().contains("SQL_VARIANT does not support string values more than 8000 length.")); + } + catch (Exception e) { + fail("Test should have failed! mistakenly inserted string value of more than 8000 in sql-variant"); + } + finally { + if (null != pstmt) { + pstmt.close(); + } + } + } + + /** + * Test ith datetime + * + * @throws SQLServerException + */ + @Test + public void testDateTime() throws SQLServerException { + java.sql.Timestamp timestamp = java.sql.Timestamp.valueOf("2007-09-23 10:10:10.0"); + tvp = new SQLServerDataTable(); + tvp.addColumnMetadata("c1", microsoft.sql.Types.SQL_VARIANT); + tvp.addRow(timestamp); + + pstmt = (SQLServerPreparedStatement) connection.prepareStatement("INSERT INTO " + destTable + " select * from ? ;"); + pstmt.setStructured(1, tvpName, tvp); + pstmt.execute(); + if (null != pstmt) { + pstmt.close(); + } + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTable); + while (rs.next()) { + assertEquals(rs.getString(1), "" + timestamp); + // System.out.println(rs.getDateTime(1));// TODO does not work + } + } + + /** + * Test with null value + * + * @throws SQLServerException + */ + @Test // TODO We need to check this later. Right now sending null with TVP is not supported + public void testNull() throws SQLServerException { + tvp = new SQLServerDataTable(); + tvp.addColumnMetadata("c1", microsoft.sql.Types.SQL_VARIANT); + try { + tvp.addRow((Date) null); + } + catch (Exception e) { + assertTrue(e.getMessage().startsWith("Inserting null value with column")); + } + + pstmt = (SQLServerPreparedStatement) connection.prepareStatement("INSERT INTO " + destTable + " select * from ? ;"); + pstmt.setStructured(1, tvpName, tvp); + pstmt.execute(); + if (null != pstmt) { + pstmt.close(); + } + rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + destTable); + while (rs.next()) { + System.out.println(rs.getString(1)); + } + } + + /** + * Test with stored procedure + * + * @throws SQLServerException + */ + @Test + public void testIntStoredProcedure() throws SQLServerException { + java.sql.Timestamp timestamp = java.sql.Timestamp.valueOf("2007-09-23 10:10:10.0"); + final String sql = "{call " + procedureName + "(?)}"; + tvp = new SQLServerDataTable(); + tvp.addColumnMetadata("c1", microsoft.sql.Types.SQL_VARIANT); + tvp.addRow(timestamp); + SQLServerCallableStatement Cstatement = (SQLServerCallableStatement) connection.prepareCall(sql); + Cstatement.setStructured(1, tvpName, tvp); + Cstatement.execute(); + rs = (SQLServerResultSet) stmt.executeQuery("select * from " + destTable); + while (rs.next()) { + System.out.println(rs.getString(1)); + } + if (null != Cstatement) { + Cstatement.close(); + } + } + + private static String[] createNumericValues() { + Boolean C1_BIT; + Short C2_TINYINT; + Short C3_SMALLINT; + Integer C4_INT; + Long C5_BIGINT; + Double C6_FLOAT; + Double C7_FLOAT; + Float C8_REAL; + BigDecimal C9_DECIMAL; + BigDecimal C10_DECIMAL; + BigDecimal C11_NUMERIC; + + boolean nullable = false; + RandomData.returnNull = nullable; + C1_BIT = RandomData.generateBoolean(nullable); + C2_TINYINT = RandomData.generateTinyint(nullable); + C3_SMALLINT = RandomData.generateSmallint(nullable); + C4_INT = RandomData.generateInt(nullable); + C5_BIGINT = RandomData.generateLong(nullable); + C6_FLOAT = RandomData.generateFloat(24, nullable); + C7_FLOAT = RandomData.generateFloat(53, nullable); + C8_REAL = RandomData.generateReal(nullable); + C9_DECIMAL = RandomData.generateDecimalNumeric(18, 0, nullable); + C10_DECIMAL = RandomData.generateDecimalNumeric(10, 5, nullable); + C11_NUMERIC = RandomData.generateDecimalNumeric(18, 0, nullable); + BigDecimal C12_NUMERIC = RandomData.generateDecimalNumeric(8, 2, nullable); + BigDecimal C13_smallMoney = RandomData.generateSmallMoney(nullable); + BigDecimal C14_money = RandomData.generateMoney(nullable); + BigDecimal C15_decimal = RandomData.generateDecimalNumeric(28, 4, nullable); + BigDecimal C16_numeric = RandomData.generateDecimalNumeric(28, 4, nullable); + + String[] numericValues = {"" + C1_BIT, "" + C2_TINYINT, "" + C3_SMALLINT, "" + C4_INT, "" + C5_BIGINT, "" + C6_FLOAT, "" + C7_FLOAT, + "" + C8_REAL, "" + C9_DECIMAL, "" + C10_DECIMAL, "" + C11_NUMERIC, "" + C12_NUMERIC, "" + C13_smallMoney, "" + C14_money, + "" + C15_decimal, "" + C16_numeric}; + + if (RandomData.returnZero && !RandomData.returnNull) { + C10_DECIMAL = new BigDecimal(0); + C12_NUMERIC = new BigDecimal(0); + C13_smallMoney = new BigDecimal(0); + C14_money = new BigDecimal(0); + C15_decimal = new BigDecimal(0); + C16_numeric = new BigDecimal(0); + } + return numericValues; + } + + @BeforeEach + private void testSetup() throws SQLException { + conn = (SQLServerConnection) DriverManager.getConnection(connectionString + ";sendStringParametersAsUnicode=true;"); + stmt = (SQLServerStatement) conn.createStatement(); + + Utils.dropProcedureIfExists(procedureName, stmt); + Utils.dropTableIfExists(destTable, stmt); + dropTVPS(); + + createTVPS(); + createTables(); + createPreocedure(); + } + + private static void dropTVPS() throws SQLException { + stmt.executeUpdate("IF EXISTS (SELECT * FROM sys.types WHERE is_table_type = 1 AND name = '" + tvpName + "') " + " drop type " + tvpName); + } + + private static void createPreocedure() throws SQLException { + String sql = "CREATE PROCEDURE " + procedureName + " @InputData " + tvpName + " READONLY " + " AS " + " BEGIN " + " INSERT INTO " + destTable + + " SELECT * FROM @InputData" + " END"; + + stmt.execute(sql); + } + + private void createTables() throws SQLException { + String sql = "create table " + destTable + " (c1 sql_variant null);"; + stmt.execute(sql); + } + + private void createTVPS() throws SQLException { + String TVPCreateCmd = "CREATE TYPE " + tvpName + " as table (c1 sql_variant null)"; + stmt.executeUpdate(TVPCreateCmd); + } + + @AfterEach + private void terminateVariation() throws SQLException { + Utils.dropProcedureIfExists(procedureName, stmt); + Utils.dropTableIfExists(destTable, stmt); + dropTVPS(); + } + + /** + * drop the tables + * + * @throws SQLException + */ + @AfterAll + public static void afterAll() throws SQLException { + if (null != stmt) { + stmt.close(); + } + + if (null != pstmt) { + pstmt.close(); + } + + if (null != rs) { + rs.close(); + } + + if (null != conn) { + conn.close(); + } + + } + +} \ No newline at end of file diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataTest.java index bfc4215715..6419d21991 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataTest.java @@ -52,7 +52,6 @@ public void testParameterMetaDataWrapper() throws SQLException { finally { Utils.dropTableIfExists(tableName, stmt); } - } } @@ -73,4 +72,27 @@ public void testSQLServerExceptionNotWrapped() throws SQLException { "SQLServerException should not be wrapped by another SQLServerException."); } } + + /** + * Test ParameterMetaData when parameter name contains braces + * + * @throws SQLException + */ + @Test + public void testNameWithBraces() throws SQLException { + try (Connection con = DriverManager.getConnection(connectionString); Statement stmt = con.createStatement()) { + + stmt.executeUpdate("create table " + tableName + " ([c1_varchar(max)] varchar(max))"); + try { + String query = "insert into " + tableName + " ([c1_varchar(max)]) values (?)"; + + try (PreparedStatement pstmt = con.prepareStatement(query)) { + pstmt.getParameterMetaData(); + } + } + finally { + Utils.dropTableIfExists(tableName, stmt); + } + } + } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataWhiteSpaceTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataWhiteSpaceTest.java new file mode 100644 index 0000000000..6911066b0d --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataWhiteSpaceTest.java @@ -0,0 +1,145 @@ +/* + * 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.parametermetadata; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.Utils; +import com.microsoft.sqlserver.testframework.util.RandomUtil; + +@RunWith(JUnitPlatform.class) +public class ParameterMetaDataWhiteSpaceTest extends AbstractTest { + private static final String tableName = "[" + RandomUtil.getIdentifier("ParameterMetaDataWhiteSpaceTest") + "]"; + + private static Statement stmt = null; + + @BeforeAll + public static void BeforeTests() throws SQLException { + connection = (SQLServerConnection) DriverManager.getConnection(connectionString); + stmt = connection.createStatement(); + createCharTable(); + } + + @AfterAll + public static void dropTables() throws SQLException { + Utils.dropTableIfExists(tableName, stmt); + + if (null != stmt) { + stmt.close(); + } + + if (null != connection) { + connection.close(); + } + } + + private static void createCharTable() throws SQLException { + stmt.execute("Create table " + tableName + " (c1 int)"); + } + + /** + * Test regular simple query + * + * @throws SQLException + */ + @Test + public void NormalTest() throws SQLException { + testUpdateWithTwoParameters("update " + tableName + " set c1 = ? where c1 = ?"); + testInsertWithOneParameter("insert into " + tableName + " (c1) values (?)"); + } + + /** + * Test query with new line character + * + * @throws SQLException + */ + @Test + public void NewLineTest() throws SQLException { + testQueriesWithWhiteSpaces("\n"); + } + + /** + * Test query with tab character + * + * @throws SQLException + */ + @Test + public void TabTest() throws SQLException { + testQueriesWithWhiteSpaces("\t"); + } + + /** + * Test query with form feed character + * + * @throws SQLException + */ + @Test + public void FormFeedTest() throws SQLException { + testQueriesWithWhiteSpaces("\f"); + } + + private void testQueriesWithWhiteSpaces(String whiteSpace) throws SQLException { + testUpdateWithTwoParameters("update" + whiteSpace + tableName + " set c1 = ? where c1 = ?"); + testUpdateWithTwoParameters("update " + tableName + " set" + whiteSpace + "c1 = ? where c1 = ?"); + testUpdateWithTwoParameters("update " + tableName + " set c1 = ? where" + whiteSpace + "c1 = ?"); + + testInsertWithOneParameter("insert into " + tableName + "(c1) values (?)"); // no space between table name and column name + testInsertWithOneParameter("insert into" + whiteSpace + tableName + " (c1) values (?)"); + } + + private void testUpdateWithTwoParameters(String sql) throws SQLException { + insertTestRow(1); + try (PreparedStatement ps = connection.prepareStatement(sql)) { + ps.setInt(1, 2); + ps.setInt(2, 1); + ps.executeUpdate(); + assertTrue(isIdPresentInTable(2), "Expected ID is not present"); + assertEquals(2, ps.getParameterMetaData().getParameterCount(), "Parameter count mismatch"); + } + } + + private void testInsertWithOneParameter(String sql) throws SQLException { + try (PreparedStatement ps = connection.prepareStatement(sql)) { + ps.setInt(1, 1); + ps.executeUpdate(); + assertTrue(isIdPresentInTable(1), "Insert statement did not work"); + assertEquals(1, ps.getParameterMetaData().getParameterCount(), "Parameter count mismatch"); + } + } + + private void insertTestRow(int id) throws SQLException { + try (PreparedStatement ps = connection.prepareStatement("insert into " + tableName + " (c1) values (?)")) { + ps.setInt(1, id); + ps.executeUpdate(); + } + } + + private boolean isIdPresentInTable(int id) throws SQLException { + try (PreparedStatement ps = connection.prepareStatement("select c1 from " + tableName + " where c1 = ?")) { + ps.setInt(1, id); + try (ResultSet rs = ps.executeQuery()) { + return rs.next(); + } + } + } +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java new file mode 100644 index 0000000000..ceb0bde9b6 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java @@ -0,0 +1,140 @@ +/* + * 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.preparedStatement; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; +import org.opentest4j.TestAbortedException; + +import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.DBConnection; +import com.microsoft.sqlserver.testframework.Utils; + +@RunWith(JUnitPlatform.class) +public class BatchExecutionWithNullTest extends AbstractTest { + + static Statement stmt = null; + static Connection connection = null; + static PreparedStatement pstmt = null; + static PreparedStatement pstmt1 = null; + 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. + * @throws SQLException + */ + @Test + public void testAddBatch2() throws SQLException { + // try { + String sPrepStmt = "insert into esimple (id, name) values (?, ?)"; + int updateCountlen = 0; + int key = 42; + + // this is the minimum sequence, I've found to trigger the error + pstmt = connection.prepareStatement(sPrepStmt); + pstmt.setInt(1, key++); + pstmt.setNull(2, Types.VARCHAR); + pstmt.addBatch(); + + pstmt.setInt(1, key++); + pstmt.setString(2, "FOO"); + pstmt.addBatch(); + + pstmt.setInt(1, key++); + pstmt.setNull(2, Types.VARCHAR); + pstmt.addBatch(); + + int[] updateCount = pstmt.executeBatch(); + updateCountlen += updateCount.length; + + pstmt.setInt(1, key++); + pstmt.setString(2, "BAR"); + pstmt.addBatch(); + + pstmt.setInt(1, key++); + pstmt.setNull(2, Types.VARCHAR); + pstmt.addBatch(); + + updateCount = pstmt.executeBatch(); + updateCountlen += updateCount.length; + + assertTrue(updateCountlen == 5, "addBatch does not add the SQL Statements to Batch ,call to addBatch failed"); + + String sPrepStmt1 = "select count(*) from esimple"; + + pstmt1 = connection.prepareStatement(sPrepStmt1); + rs = pstmt1.executeQuery(); + rs.next(); + assertTrue(rs.getInt(1) == 5, "affected rows does not match with batch size. Insert failed"); + pstmt1.close(); + + } + + /** + * Tests the same as addBatch2, only with AE on the connection string + * + * @throws SQLException + */ + @Test + public void testAddbatch2AEOnConnection() throws SQLException { + connection = DriverManager.getConnection(connectionString + ";columnEncryptionSetting=Enabled;"); + testAddBatch2(); + } + + @BeforeEach + public void testSetup() throws TestAbortedException, Exception { + assumeTrue(13 <= new DBConnection(connectionString).getServerVersion(), + "Aborting test case as SQL Server version is not compatible with Always encrypted "); + + connection = DriverManager.getConnection(connectionString); + SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); + Utils.dropTableIfExists("esimple", stmt); + String sql1 = "create table esimple (id integer not null, name varchar(255), constraint pk_esimple primary key (id))"; + stmt.execute(sql1); + stmt.close(); + } + + @AfterAll + public static void terminateVariation() throws SQLException { + + SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); + Utils.dropTableIfExists("esimple", stmt); + + if (null != connection) { + connection.close(); + } + if (null != pstmt) { + pstmt.close(); + } + if (null != pstmt1) { + pstmt1.close(); + } + if (null != stmt) { + stmt.close(); + } + if (null != rs) { + rs.close(); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java new file mode 100644 index 0000000000..05f36bdee2 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java @@ -0,0 +1,436 @@ +/* + * 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.preparedStatement; + +import static org.junit.jupiter.api.Assertions.fail; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; +import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.Utils; + +/** + * Tests with sql queries using preparedStatement without parameters + * + * + */ +@RunWith(JUnitPlatform.class) +public class RegressionTest extends AbstractTest { + static Connection con = null; + static PreparedStatement pstmt1 = null; + static PreparedStatement pstmt2 = null; + static PreparedStatement pstmt3 = null; + static PreparedStatement pstmt4 = null; + + /** + * Setup before test + * + * @throws SQLException + */ + @BeforeAll + public static void setupTest() throws SQLException { + con = DriverManager.getConnection(connectionString); + Statement stmt = con.createStatement(); + Utils.dropTableIfExists("x", stmt); + if (null != stmt) { + stmt.close(); + } + } + + /** + * Tests creating view using preparedStatement + * + * @throws SQLException + */ + @Test + public void createViewTest() throws SQLException { + try { + pstmt1 = con.prepareStatement("create view x as select 1 a"); + pstmt2 = con.prepareStatement("drop view x"); + pstmt1.execute(); + pstmt2.execute(); + } + catch (SQLException e) { + fail("Create/drop view with preparedStatement failed! Error message: " + e.getMessage()); + } + + finally { + if (null != pstmt1) { + pstmt1.close(); + } + if (null != pstmt2) { + pstmt2.close(); + } + } + } + + /** + * Tests creating schema using preparedStatement + * + * @throws SQLException + */ + @Test + public void createSchemaTest() throws SQLException { + try { + pstmt1 = con.prepareStatement("create schema x"); + pstmt2 = con.prepareStatement("drop schema x"); + pstmt1.execute(); + pstmt2.execute(); + } + catch (SQLException e) { + fail("Create/drop schema with preparedStatement failed! Error message:" + e.getMessage()); + } + + finally { + if (null != pstmt1) { + pstmt1.close(); + } + if (null != pstmt2) { + pstmt2.close(); + } + } + } + + /** + * Test creating and dropping tabel with preparedStatement + * + * @throws SQLException + */ + @Test + public void createTableTest() throws SQLException { + try { + pstmt1 = con.prepareStatement("create table x (col1 int)"); + pstmt2 = con.prepareStatement("drop table x"); + pstmt1.execute(); + pstmt2.execute(); + } + catch (SQLException e) { + fail("Create/drop table with preparedStatement failed! Error message:" + e.getMessage()); + } + + finally { + if (null != pstmt1) { + pstmt1.close(); + } + if (null != pstmt2) { + pstmt2.close(); + } + } + } + + /** + * Tests creating/altering/dropping table + * + * @throws SQLException + */ + @Test + public void alterTableTest() throws SQLException { + try { + pstmt1 = con.prepareStatement("create table x (col1 int)"); + pstmt2 = con.prepareStatement("ALTER TABLE x ADD column_name char;"); + pstmt3 = con.prepareStatement("drop table x"); + pstmt1.execute(); + pstmt2.execute(); + pstmt3.execute(); + } + catch (SQLException e) { + fail("Create/drop/alter table with preparedStatement failed! Error message:" + e.getMessage()); + } + + finally { + if (null != pstmt1) { + pstmt1.close(); + } + if (null != pstmt2) { + pstmt2.close(); + } + if (null != pstmt3) { + pstmt3.close(); + } + } + } + + /** + * Tests with grant queries + * + * @throws SQLException + */ + @Test + public void grantTest() throws SQLException { + try { + pstmt1 = con.prepareStatement("create table x (col1 int)"); + pstmt2 = con.prepareStatement("grant select on x to public"); + pstmt3 = con.prepareStatement("revoke select on x from public"); + pstmt4 = con.prepareStatement("drop table x"); + pstmt1.execute(); + pstmt2.execute(); + pstmt3.execute(); + pstmt4.execute(); + } + catch (SQLException e) { + fail("grant with preparedStatement failed! Error message:" + e.getMessage()); + } + + finally { + if (null != pstmt1) { + pstmt1.close(); + } + if (null != pstmt2) { + pstmt2.close(); + } + if (null != pstmt3) { + pstmt3.close(); + } + if (null != pstmt4) { + pstmt4.close(); + } + } + } + + /** + * Test with large string and batch + * + * @throws SQLException + */ + @Test + public void batchWithLargeStringTest() throws SQLException { + Statement stmt = con.createStatement(); + PreparedStatement pstmt = null; + ResultSet rs = null; + Utils.dropTableIfExists("TEST_TABLE", stmt); + + con.setAutoCommit(false); + + // create a table with two columns + boolean createPrimaryKey = false; + try { + stmt.execute("if object_id('TEST_TABLE', 'U') is not null\ndrop table TEST_TABLE;"); + if (createPrimaryKey) { + stmt.execute("create table TEST_TABLE ( ID int, DATA nvarchar(max), primary key (ID) );"); + } + else { + stmt.execute("create table TEST_TABLE ( ID int, DATA nvarchar(max) );"); + } + } + catch (Exception e) { + fail(e.toString()); + } + + con.commit(); + + // build a String with 4001 characters + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < 4001; i++) { + stringBuilder.append('c'); + } + String largeString = stringBuilder.toString(); + + String[] values = {"a", "b", largeString, "d", "e"}; + // insert five rows into the table; use a batch for each row + try { + pstmt = con.prepareStatement("insert into TEST_TABLE values (?,?)"); + // 0,a + pstmt.setInt(1, 0); + pstmt.setNString(2, values[0]); + pstmt.addBatch(); + + // 1,b + pstmt.setInt(1, 1); + pstmt.setNString(2, values[1]); + pstmt.addBatch(); + + // 2,ccc... + pstmt.setInt(1, 2); + pstmt.setNString(2, values[2]); + pstmt.addBatch(); + + // 3,d + pstmt.setInt(1, 3); + pstmt.setNString(2, values[3]); + pstmt.addBatch(); + + // 4,e + pstmt.setInt(1, 4); + pstmt.setNString(2, values[4]); + pstmt.addBatch(); + + pstmt.executeBatch(); + } + catch (Exception e) { + fail(e.toString()); + } + connection.commit(); + + // check the data in the table + Map selectedValues = new LinkedHashMap<>(); + int id = 0; + try { + pstmt = con.prepareStatement("select * from TEST_TABLE;"); + try { + rs = pstmt.executeQuery(); + int i = 0; + while (rs.next()) { + id = rs.getInt(1); + String data = rs.getNString(2); + if (selectedValues.containsKey(id)) { + fail("Found duplicate id: " + id + " ,actual values is : " + values[i++] + " data is: " + data); + } + selectedValues.put(id, data); + } + } + finally { + if (null != rs) { + rs.close(); + } + } + } + finally { + Utils.dropTableIfExists("TEST_TABLE", stmt); + if (null != pstmt) { + pstmt.close(); + } + if (null != stmt) { + stmt.close(); + } + } + + } + + /** + * Test with large string and tests with more batch queries + * + * @throws SQLException + */ + @Test + public void addBatchWithLargeStringTest() throws SQLException { + Statement stmt = con.createStatement(); + PreparedStatement pstmt = null; + Utils.dropTableIfExists("TEST_TABLE", stmt); + + con.setAutoCommit(false); + + // create a table with two columns + boolean createPrimaryKey = false; + try { + stmt.execute("if object_id('testTable', 'U') is not null\ndrop table testTable;"); + if (createPrimaryKey) { + stmt.execute("create table testTable ( ID int, DATA nvarchar(max), primary key (ID) );"); + } + else { + stmt.execute("create table testTable ( ID int, DATA nvarchar(max) );"); + } + } + catch (Exception e) { + fail(e.toString()); + } + con.commit(); + + // build a String with 4001 characters + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < 4001; i++) { + stringBuilder.append('x'); + } + String largeString = stringBuilder.toString(); + + // insert five rows into the table; use a batch for each row + try { + pstmt = con.prepareStatement("insert into testTable values (?,?), (?,?);"); + // 0,a + // 1,b + pstmt.setInt(1, 0); + pstmt.setNString(2, "a"); + pstmt.setInt(3, 1); + pstmt.setNString(4, "b"); + pstmt.addBatch(); + + // 2,c + // 3,d + pstmt.setInt(1, 2); + pstmt.setNString(2, "c"); + pstmt.setInt(3, 3); + pstmt.setNString(4, "d"); + pstmt.addBatch(); + + // 4,xxx... + // 5,f + pstmt.setInt(1, 4); + pstmt.setNString(2, largeString); + pstmt.setInt(3, 5); + pstmt.setNString(4, "f"); + pstmt.addBatch(); + + // 6,g + // 7,h + pstmt.setInt(1, 6); + pstmt.setNString(2, "g"); + pstmt.setInt(3, 7); + pstmt.setNString(4, "h"); + pstmt.addBatch(); + + // 8,i + // 9,xxx... + pstmt.setInt(1, 8); + pstmt.setNString(2, "i"); + pstmt.setInt(3, 9); + pstmt.setNString(4, largeString); + pstmt.addBatch(); + + pstmt.executeBatch(); + + con.commit(); + } + + catch (Exception e) { + fail(e.toString()); + } + finally { + Utils.dropTableIfExists("testTable", stmt); + if (null != stmt) { + stmt.close(); + } + } + } + + /** + * Cleanup after test + * + * @throws SQLException + */ + @AfterAll + public static void cleanup() throws SQLException { + Statement stmt = con.createStatement(); + Utils.dropTableIfExists("x", stmt); + Utils.dropTableIfExists("TEST_TABLE", stmt); + if (null != stmt) { + stmt.close(); + } + if (null != con) { + con.close(); + } + if (null != pstmt1) { + pstmt1.close(); + } + if (null != pstmt2) { + pstmt2.close(); + } + + } + +} \ No newline at end of file diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java index 08d9357030..e33fc2f1ec 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java @@ -7,17 +7,24 @@ */ package com.microsoft.sqlserver.jdbc.resultset; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; +import java.math.BigDecimal; +import java.sql.Blob; +import java.sql.Clob; import java.sql.Connection; import java.sql.DriverManager; +import java.sql.NClob; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; +import java.sql.SQLXML; import java.sql.Statement; +import java.util.UUID; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; @@ -35,47 +42,201 @@ public class ResultSetTest extends AbstractTest { /** * Tests proper exception for unsupported operation * - * @throws Exception + * @throws SQLException */ @Test - public void testJdbc41ResultSetMethods() throws Exception { - Connection con = DriverManager.getConnection(connectionString); - Statement stmt = con.createStatement(); - try { - stmt.executeUpdate("create table " + tableName + " (col1 int, col2 text, col3 int identity(1,1) primary key)"); + public void testJdbc41ResultSetMethods() throws SQLException { + try (Connection con = DriverManager.getConnection(connectionString); + Statement stmt = con.createStatement()) { + stmt.executeUpdate("create table " + tableName + " ( " + + "col1 int, " + + "col2 varchar(512), " + + "col3 float, " + + "col4 decimal(10,5), " + + "col5 uniqueidentifier, " + + "col6 xml, " + + "col7 varbinary(max), " + + "col8 text, " + + "col9 ntext, " + + "col10 varbinary(max), " + + "col11 date, " + + "col12 time, " + + "col13 datetime2, " + + "col14 datetimeoffset, " + + "order_column int identity(1,1) primary key)"); + try { + + stmt.executeUpdate("Insert into " + tableName + " values(" + + "1, " // col1 + + "'hello', " // col2 + + "2.0, " // col3 + + "123.45, " // col4 + + "'6F9619FF-8B86-D011-B42D-00C04FC964FF', " // col5 + + "'', " // col6 + + "0x63C34D6BCAD555EB64BF7E848D02C376, " // col7 + + "'text', " // col8 + + "'ntext', " // col9 + + "0x63C34D6BCAD555EB64BF7E848D02C376," // col10 + + "'2017-05-19'," // col11 + + "'10:47:15.1234567'," // col12 + + "'2017-05-19T10:47:15.1234567'," // col13 + + "'2017-05-19T10:47:15.1234567+02:00'" // col14 + + ")"); + + stmt.executeUpdate("Insert into " + tableName + " values(" + + "null, " + + "null, " + + "null, " + + "null, " + + "null, " + + "null, " + + "null, " + + "null, " + + "null, " + + "null, " + + "null, " + + "null, " + + "null, " + + "null)"); + + try (ResultSet rs = stmt.executeQuery("select * from " + tableName + " order by order_column")) { + // test non-null values + assertTrue(rs.next()); + assertEquals(Byte.valueOf((byte) 1), rs.getObject(1, Byte.class)); + assertEquals(Byte.valueOf((byte) 1), rs.getObject("col1", Byte.class)); + assertEquals(Short.valueOf((short) 1), rs.getObject(1, Short.class)); + assertEquals(Short.valueOf((short) 1), rs.getObject("col1", Short.class)); + assertEquals(Integer.valueOf(1), rs.getObject(1, Integer.class)); + assertEquals(Integer.valueOf(1), rs.getObject("col1", Integer.class)); + assertEquals(Long.valueOf(1), rs.getObject(1, Long.class)); + assertEquals(Long.valueOf(1), rs.getObject("col1", Long.class)); + assertEquals(Boolean.TRUE, rs.getObject(1, Boolean.class)); + assertEquals(Boolean.TRUE, rs.getObject("col1", Boolean.class)); - stmt.executeUpdate("Insert into " + tableName + " values(0, 'hello')"); + assertEquals("hello", rs.getObject(2, String.class)); + assertEquals("hello", rs.getObject("col2", String.class)); - stmt.executeUpdate("Insert into " + tableName + " values(0, 'yo')"); + assertEquals(2.0f, rs.getObject(3, Float.class), 0.0001f); + assertEquals(2.0f, rs.getObject("col3", Float.class), 0.0001f); + assertEquals(2.0d, rs.getObject(3, Double.class), 0.0001d); + assertEquals(2.0d, rs.getObject("col3", Double.class), 0.0001d); - ResultSet rs = stmt.executeQuery("select * from " + tableName); - rs.next(); - // Both methods throw exceptions - try { + // BigDecimal#equals considers the number of decimal places + assertEquals(0, rs.getObject(4, BigDecimal.class).compareTo(new BigDecimal("123.45"))); + assertEquals(0, rs.getObject("col4", BigDecimal.class).compareTo(new BigDecimal("123.45"))); - int col1 = rs.getObject(1, Integer.class); - } - catch (Exception e) { - // unsupported feature - assertEquals(e.getClass(), SQLFeatureNotSupportedException.class, "Verify exception type: " + e.getMessage()); - } - try { - String col2 = rs.getObject("col2", String.class); - } - catch (Exception e) { - // unsupported feature - assertEquals(e.getClass(), SQLFeatureNotSupportedException.class, "Verify exception type: " + e.getMessage()); - } - try { + assertEquals(UUID.fromString("6F9619FF-8B86-D011-B42D-00C04FC964FF"), rs.getObject(5, UUID.class)); + assertEquals(UUID.fromString("6F9619FF-8B86-D011-B42D-00C04FC964FF"), rs.getObject("col5", UUID.class)); + + SQLXML sqlXml; + sqlXml = rs.getObject(6, SQLXML.class); + try { + assertEquals("", sqlXml.getString()); + } finally { + sqlXml.free(); + } + + Blob blob; + blob = rs.getObject(7, Blob.class); + try { + assertArrayEquals(new byte[] {0x63, (byte) 0xC3, 0x4D, 0x6B, (byte) 0xCA, (byte) 0xD5, 0x55, (byte) 0xEB, 0x64, (byte) 0xBF, 0x7E, (byte) 0x84, (byte) 0x8D, 0x02, (byte) 0xC3, 0x76}, + blob.getBytes(1, 16)); + } finally { + blob.free(); + } + + Clob clob; + clob = rs.getObject(8, Clob.class); + try { + assertEquals("text", clob.getSubString(1, 4)); + } finally { + clob.free(); + } + + NClob nclob; + nclob = rs.getObject(9, NClob.class); + try { + assertEquals("ntext", nclob.getSubString(1, 5)); + } finally { + nclob.free(); + } + + assertArrayEquals(new byte[] {0x63, (byte) 0xC3, 0x4D, 0x6B, (byte) 0xCA, (byte) 0xD5, 0x55, (byte) 0xEB, 0x64, (byte) 0xBF, 0x7E, (byte) 0x84, (byte) 0x8D, 0x02, (byte) 0xC3, 0x76}, + rs.getObject(10, byte[].class)); + + assertEquals(java.sql.Date.valueOf("2017-05-19"), rs.getObject(11, java.sql.Date.class)); + assertEquals(java.sql.Date.valueOf("2017-05-19"), rs.getObject("col11", java.sql.Date.class)); + + java.sql.Time expectedTime = new java.sql.Time(java.sql.Time.valueOf("10:47:15").getTime() + 123L); + assertEquals(expectedTime, rs.getObject(12, java.sql.Time.class)); + assertEquals(expectedTime, rs.getObject("col12", java.sql.Time.class)); + + assertEquals(java.sql.Timestamp.valueOf("2017-05-19 10:47:15.1234567"), rs.getObject(13, java.sql.Timestamp.class)); + assertEquals(java.sql.Timestamp.valueOf("2017-05-19 10:47:15.1234567"), rs.getObject("col13", java.sql.Timestamp.class)); + + assertEquals("2017-05-19 10:47:15.1234567 +02:00", rs.getObject(14, microsoft.sql.DateTimeOffset.class).toString()); + assertEquals("2017-05-19 10:47:15.1234567 +02:00", rs.getObject("col14", microsoft.sql.DateTimeOffset.class).toString()); + + + // test null values, mostly to verify primitive wrappers do not return default values + assertTrue(rs.next()); + assertNull(rs.getObject("col1", Boolean.class)); + assertNull(rs.getObject(1, Boolean.class)); + assertNull(rs.getObject("col1", Byte.class)); + assertNull(rs.getObject(1, Byte.class)); + assertNull(rs.getObject("col1", Short.class)); + assertNull(rs.getObject(1, Short.class)); + assertNull(rs.getObject(1, Integer.class)); + assertNull(rs.getObject("col1", Integer.class)); + assertNull(rs.getObject(1, Long.class)); + assertNull(rs.getObject("col1", Long.class)); + + assertNull(rs.getObject(2, String.class)); + assertNull(rs.getObject("col2", String.class)); + + assertNull(rs.getObject(3, Float.class)); + assertNull(rs.getObject("col3", Float.class)); + assertNull(rs.getObject(3, Double.class)); + assertNull(rs.getObject("col3", Double.class)); + + assertNull(rs.getObject(4, BigDecimal.class)); + assertNull(rs.getObject("col4", BigDecimal.class)); + + assertNull(rs.getObject(5, UUID.class)); + assertNull(rs.getObject("col5", UUID.class)); + + assertNull(rs.getObject(6, SQLXML.class)); + assertNull(rs.getObject("col6", SQLXML.class)); + + assertNull(rs.getObject(7, Blob.class)); + assertNull(rs.getObject("col7", Blob.class)); + + assertNull(rs.getObject(8, Clob.class)); + assertNull(rs.getObject("col8", Clob.class)); + + assertNull(rs.getObject(9, NClob.class)); + assertNull(rs.getObject("col9", NClob.class)); + + assertNull(rs.getObject(10, byte[].class)); + assertNull(rs.getObject("col10", byte[].class)); + + assertNull(rs.getObject(11, java.sql.Date.class)); + assertNull(rs.getObject("col11", java.sql.Date.class)); + + assertNull(rs.getObject(12, java.sql.Time.class)); + assertNull(rs.getObject("col12", java.sql.Time.class)); + + assertNull(rs.getObject(13, java.sql.Timestamp.class)); + assertNull(rs.getObject("col14", java.sql.Timestamp.class)); + + assertNull(rs.getObject(14, microsoft.sql.DateTimeOffset.class)); + assertNull(rs.getObject("col14", microsoft.sql.DateTimeOffset.class)); + + assertFalse(rs.next()); + } + } finally { stmt.executeUpdate("drop table " + tableName); } - catch (Exception ex) { - fail(ex.toString()); - } - } - finally { - stmt.close(); - con.close(); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PQImpsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PQImpsTest.java index c2d24c7f88..15fce3fe58 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PQImpsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PQImpsTest.java @@ -8,8 +8,8 @@ package com.microsoft.sqlserver.jdbc.unit.statement; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.sql.DriverManager; import java.sql.ParameterMetaData; @@ -27,6 +27,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerConnection; import com.microsoft.sqlserver.jdbc.SQLServerException; +import com.microsoft.sqlserver.jdbc.SQLServerParameterMetaData; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.util.RandomUtil; @@ -51,13 +52,15 @@ public class PQImpsTest extends AbstractTest { private static String mergeNameDesTable = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("mergeNameDesTable_DB")); private static String numericTable = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("numericTable_DB")); private static String charTable = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("charTable_DB")); + private static String charTable2 = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("charTable2_DB")); private static String binaryTable = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("binaryTable_DB")); private static String dateAndTimeTable = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("dateAndTimeTable_DB")); private static String multipleTypesTable = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("multipleTypesTable_DB")); private static String spaceTable = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("spaceTable_DB")); - + /** * Setup + * * @throws SQLException */ @BeforeAll @@ -68,6 +71,7 @@ public static void BeforeTests() throws SQLException { createMultipleTypesTable(); createNumericTable(); createCharTable(); + createChar2Table(); createBinaryTable(); createDateAndTimeTable(); createTablesForCompexQueries(); @@ -77,6 +81,7 @@ public static void BeforeTests() throws SQLException { /** * Numeric types test + * * @throws SQLException */ @Test @@ -104,6 +109,7 @@ public void numericTest() throws SQLException { /** * Char types test + * * @throws SQLException */ @Test @@ -131,6 +137,7 @@ public void charTests() throws SQLException { /** * Binary types test + * * @throws SQLException */ @Test @@ -159,6 +166,7 @@ public void binaryTests() throws SQLException { /** * Temporal types test + * * @throws SQLException */ @Test @@ -187,6 +195,7 @@ public void temporalTests() throws SQLException { /** * Multiple Types table + * * @throws Exception */ @Test @@ -416,12 +425,16 @@ private static void createCharTable() throws SQLException { stmt.execute("Create table " + charTable + " (" + "c1 char(50) not null," + "c2 varchar(20) not null," + "c3 nchar(30) not null," + "c4 nvarchar(60) not null," + "c5 text not null," + "c6 ntext not null" + ")"); } - + private static void createSpaceTable() throws SQLException { stmt.execute("Create table " + spaceTable + " (" + "[c1*/someString withspace] char(50) not null," + "c2 varchar(20) not null," + "c3 nchar(30) not null," + "c4 nvarchar(60) not null," + "c5 text not null," + "c6 ntext not null" + ")"); } + private static void createChar2Table() throws SQLException { + stmt.execute("Create table " + charTable2 + " (" + "table2c1 char(50) not null)"); + } + private static void populateCharTable() throws SQLException { stmt.execute("insert into " + charTable + " values (" + "'Hello'," + "'Hello'," + "N'Hello'," + "N'Hello'," + "'Hello'," + "N'Hello'" + ")"); } @@ -714,6 +727,7 @@ private static void populateTablesForCompexQueries() throws SQLException { /** * Test subquery + * * @throws SQLException */ @Test @@ -743,6 +757,7 @@ public void testSubquery() throws SQLException { /** * Test join + * * @throws SQLException */ @Test @@ -773,7 +788,8 @@ public void testJoin() throws SQLException { } /** - * Test merge + * Test merge + * * @throws SQLException */ @Test @@ -972,6 +988,7 @@ private static void testMixedWithHardcodedValues() throws SQLException { /** * Test Orderby + * * @throws SQLException */ @Test @@ -1001,6 +1018,7 @@ public void testOrderBy() throws SQLException { /** * Test Groupby + * * @throws SQLException */ @Test @@ -1030,6 +1048,7 @@ private void testGroupBy() throws SQLException { /** * Test Lower + * * @throws SQLException */ @Test @@ -1058,6 +1077,7 @@ public void testLower() throws SQLException { /** * Test Power + * * @throws SQLException */ @Test @@ -1085,6 +1105,7 @@ public void testPower() throws SQLException { /** * All in one queries + * * @throws SQLException */ @Test @@ -1115,7 +1136,7 @@ public void testAllInOneQuery() throws SQLException { compareParameterMetaData(pmd, 3, "java.lang.Integer", 4, "int", 10, 0); } } - + /** * test query with simple multiple line comments * @@ -1154,7 +1175,7 @@ public void testQueryWithMultipleLineComments2() throws SQLException { fail(e.toString()); } } - + /** * test insertion query with multiple line comments * @@ -1163,7 +1184,7 @@ public void testQueryWithMultipleLineComments2() throws SQLException { @Test public void testQueryWithMultipleLineCommentsInsert() throws SQLException { pstmt = connection.prepareStatement("/*te\nst*//*test*/insert /*test*/into " + charTable + " (c1) VALUES(?)"); - + try { pstmt.getParameterMetaData(); } @@ -1171,7 +1192,7 @@ public void testQueryWithMultipleLineCommentsInsert() throws SQLException { fail(e.toString()); } } - + /** * test update query with multiple line comments * @@ -1180,7 +1201,7 @@ public void testQueryWithMultipleLineCommentsInsert() throws SQLException { @Test public void testQueryWithMultipleLineCommentsUpdate() throws SQLException { pstmt = connection.prepareStatement("/*te\nst*//*test*/update /*test*/" + charTable + " set c1=123 where c1=?"); - + try { pstmt.getParameterMetaData(); } @@ -1188,7 +1209,7 @@ public void testQueryWithMultipleLineCommentsUpdate() throws SQLException { fail(e.toString()); } } - + /** * test deletion query with multiple line comments * @@ -1197,7 +1218,7 @@ public void testQueryWithMultipleLineCommentsUpdate() throws SQLException { @Test public void testQueryWithMultipleLineCommentsDeletion() throws SQLException { pstmt = connection.prepareStatement("/*te\nst*//*test*/delete /*test*/from " + charTable + " where c1=?"); - + try { pstmt.getParameterMetaData(); } @@ -1262,7 +1283,7 @@ public void testQueryWithSingleLineComments3() throws SQLException { fail(e.toString()); } } - + /** * test insertion query with single line comments * @@ -1271,7 +1292,7 @@ public void testQueryWithSingleLineComments3() throws SQLException { @Test public void testQueryWithSingleLineCommentsInsert() throws SQLException { pstmt = connection.prepareStatement("--#test\ninsert /*test*/into " + charTable + " (c1) VALUES(?)"); - + try { pstmt.getParameterMetaData(); } @@ -1279,7 +1300,7 @@ public void testQueryWithSingleLineCommentsInsert() throws SQLException { fail(e.toString()); } } - + /** * test update query with single line comments * @@ -1288,7 +1309,7 @@ public void testQueryWithSingleLineCommentsInsert() throws SQLException { @Test public void testQueryWithSingleLineCommentsUpdate() throws SQLException { pstmt = connection.prepareStatement("--#test\nupdate /*test*/" + charTable + " set c1=123 where c1=?"); - + try { pstmt.getParameterMetaData(); } @@ -1296,7 +1317,7 @@ public void testQueryWithSingleLineCommentsUpdate() throws SQLException { fail(e.toString()); } } - + /** * test deletion query with single line comments * @@ -1305,7 +1326,7 @@ public void testQueryWithSingleLineCommentsUpdate() throws SQLException { @Test public void testQueryWithSingleLineCommentsDeletion() throws SQLException { pstmt = connection.prepareStatement("--#test\ndelete /*test*/from " + charTable + " where c1=?"); - + try { pstmt.getParameterMetaData(); } @@ -1313,7 +1334,7 @@ public void testQueryWithSingleLineCommentsDeletion() throws SQLException { fail(e.toString()); } } - + /** * test column name with end comment mark and space * @@ -1331,8 +1352,30 @@ public void testQueryWithSpaceAndEndCommentMarkInColumnName() throws SQLServerEx } } + /** + * test getting parameter count with a complex query with multiple table + * + * @throws SQLServerException + */ + @Test + public void testComplexQueryWithMultipleTables() throws SQLServerException { + pstmt = connection.prepareStatement( + "insert into " + charTable + " (c1) select ? where not exists (select * from " + charTable2 + " where table2c1 = ?)"); + + try { + SQLServerParameterMetaData pMD = (SQLServerParameterMetaData) pstmt.getParameterMetaData(); + int parameterCount = pMD.getParameterCount(); + + assertTrue(2 == parameterCount, "Parameter Count should be 2."); + } + catch (Exception e) { + fail(e.toString()); + } + } + /** * Cleanup + * * @throws SQLException */ @AfterAll @@ -1342,6 +1385,7 @@ public static void dropTables() throws SQLException { stmt.execute("if object_id('" + mergeNameDesTable + "','U') is not null" + " drop table " + mergeNameDesTable); stmt.execute("if object_id('" + numericTable + "','U') is not null" + " drop table " + numericTable); stmt.execute("if object_id('" + charTable + "','U') is not null" + " drop table " + charTable); + stmt.execute("if object_id('" + charTable2 + "','U') is not null" + " drop table " + charTable2); stmt.execute("if object_id('" + binaryTable + "','U') is not null" + " drop table " + binaryTable); stmt.execute("if object_id('" + dateAndTimeTable + "','U') is not null" + " drop table " + dateAndTimeTable); stmt.execute("if object_id('" + multipleTypesTable + "','U') is not null" + " drop table " + multipleTypesTable); 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 da7e3eab18..4418a797e0 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 @@ -7,22 +7,29 @@ */ package com.microsoft.sqlserver.jdbc.unit.statement; +import static org.junit.Assert.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; import java.io.StringReader; +import java.math.BigDecimal; +import java.sql.Blob; import java.sql.CallableStatement; +import java.sql.Clob; import java.sql.Connection; import java.sql.DriverManager; +import java.sql.NClob; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.SQLXML; import java.sql.Statement; import java.sql.Types; import java.util.ArrayList; import java.util.Random; +import java.util.UUID; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -1134,7 +1141,7 @@ public void testLargeMaxRows_JDBC42() throws Exception { // SQL Server only supports integer limits for setting max rows // If the value MAX_VALUE + 1 is accepted, throw exception try { - newValue = new Long(java.lang.Integer.MAX_VALUE) + 1; + newValue = (long) Integer.MAX_VALUE + 1; dbstmt.setLargeMaxRows(newValue); throw new SQLException("setLargeMaxRows(): Long values should not be set"); } @@ -1177,55 +1184,133 @@ public class TCStatementCallable { */ @Test public void testJdbc41CallableStatementMethods() throws Exception { - assumeTrue("JDBC41".equals(Utils.getConfiguredProperty("JDBC_Version")), "Aborting test case as JDBC version is not compatible. "); Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); // Prepare database setup String name = RandomUtil.getIdentifier("p1"); String procName = AbstractSQLGenerator.escapeIdentifier(name); - Connection conn = DriverManager.getConnection(connectionString); - Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); - try { - Utils.dropProcedureIfExists(procName, stmt); - } - catch (Exception ex) { - } - ; - String query = "create procedure " + procName - + " @col1Value varchar(512) OUTPUT, @col2Value varchar(512) OUTPUT AS BEGIN SET @col1Value='hello' SET @col2Value='world' END"; - stmt.execute(query); + try (Connection conn = DriverManager.getConnection(connectionString); + Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)) { + String query = "create procedure " + procName + + " @col1Value varchar(512) OUTPUT," + + " @col2Value int OUTPUT," + + " @col3Value float OUTPUT," + + " @col4Value decimal(10,5) OUTPUT," + + " @col5Value uniqueidentifier OUTPUT," + + " @col6Value xml OUTPUT," + + " @col7Value varbinary(max) OUTPUT," + + " @col8Value text OUTPUT," + + " @col9Value ntext OUTPUT," + + " @col10Value varbinary(max) OUTPUT," + + " @col11Value date OUTPUT," + + " @col12Value time OUTPUT," + + " @col13Value datetime2 OUTPUT," + + " @col14Value datetimeoffset OUTPUT" + + " AS BEGIN " + + " SET @col1Value = 'hello'" + + " SET @col2Value = 1" + + " SET @col3Value = 2.0" + + " SET @col4Value = 123.45" + + " SET @col5Value = '6F9619FF-8B86-D011-B42D-00C04FC964FF'" + + " SET @col6Value = ''" + + " SET @col7Value = 0x63C34D6BCAD555EB64BF7E848D02C376" + + " SET @col8Value = 'text'" + + " SET @col9Value = 'ntext'" + + " SET @col10Value = 0x63C34D6BCAD555EB64BF7E848D02C376" + + " SET @col11Value = '2017-05-19'" + + " SET @col12Value = '10:47:15.1234567'" + + " SET @col13Value = '2017-05-19T10:47:15.1234567'" + + " SET @col14Value = '2017-05-19T10:47:15.1234567+02:00'" + + " END"; + stmt.execute(query); + + // Test JDBC 4.1 methods for CallableStatement + try (CallableStatement cstmt = conn.prepareCall("{call " + procName + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)}")) { + cstmt.registerOutParameter(1, java.sql.Types.VARCHAR); + cstmt.registerOutParameter(2, java.sql.Types.INTEGER); + cstmt.registerOutParameter(3, java.sql.Types.FLOAT); + cstmt.registerOutParameter(4, java.sql.Types.DECIMAL); + cstmt.registerOutParameter(5, microsoft.sql.Types.GUID); + cstmt.registerOutParameter(6, java.sql.Types.SQLXML); + cstmt.registerOutParameter(7, java.sql.Types.VARBINARY); + cstmt.registerOutParameter(8, java.sql.Types.CLOB); + cstmt.registerOutParameter(9, java.sql.Types.NCLOB); + cstmt.registerOutParameter(10, java.sql.Types.VARBINARY); + cstmt.registerOutParameter(11, java.sql.Types.DATE); + cstmt.registerOutParameter(12, java.sql.Types.TIME); + cstmt.registerOutParameter(13, java.sql.Types.TIMESTAMP); + cstmt.registerOutParameter(14, java.sql.Types.TIMESTAMP_WITH_TIMEZONE); + cstmt.execute(); + + assertEquals("hello", cstmt.getObject(1, String.class)); + assertEquals("hello", cstmt.getObject("col1Value", String.class)); + + assertEquals(Integer.valueOf(1), cstmt.getObject(2, Integer.class)); + assertEquals(Integer.valueOf(1), cstmt.getObject("col2Value", Integer.class)); + + assertEquals(2.0f, cstmt.getObject(3, Float.class), 0.0001f); + assertEquals(2.0f, cstmt.getObject("col3Value", Float.class), 0.0001f); + assertEquals(2.0d, cstmt.getObject(3, Double.class), 0.0001d); + assertEquals(2.0d, cstmt.getObject("col3Value", Double.class), 0.0001d); + + // BigDecimal#equals considers the number of decimal places + assertEquals(0, cstmt.getObject(4, BigDecimal.class).compareTo(new BigDecimal("123.45"))); + assertEquals(0, cstmt.getObject("col4Value", BigDecimal.class).compareTo(new BigDecimal("123.45"))); + + assertEquals(UUID.fromString("6F9619FF-8B86-D011-B42D-00C04FC964FF"), cstmt.getObject(5, UUID.class)); + assertEquals(UUID.fromString("6F9619FF-8B86-D011-B42D-00C04FC964FF"), cstmt.getObject("col5Value", UUID.class)); + + SQLXML sqlXml; + sqlXml = cstmt.getObject(6, SQLXML.class); + try { + assertEquals("", sqlXml.getString()); + } finally { + sqlXml.free(); + } - // Test JDBC 4.1 methods for CallableStatement - CallableStatement cstmt = conn.prepareCall("{call " + procName + "(?, ?)}"); - cstmt.registerOutParameter(1, java.sql.Types.VARCHAR); - cstmt.registerOutParameter(2, java.sql.Types.VARCHAR); - cstmt.execute(); + Blob blob; + blob = cstmt.getObject(7, Blob.class); + try { + assertArrayEquals(new byte[] {0x63, (byte) 0xC3, 0x4D, 0x6B, (byte) 0xCA, (byte) 0xD5, 0x55, (byte) 0xEB, 0x64, (byte) 0xBF, 0x7E, (byte) 0x84, (byte) 0x8D, 0x02, (byte) 0xC3, 0x76}, + blob.getBytes(1, 16)); + } finally { + blob.free(); + } - try { - String out1 = cstmt.getObject(1, String.class); - } - catch (Exception e) { + Clob clob; + clob = cstmt.getObject(8, Clob.class); + try { + assertEquals("text", clob.getSubString(1, 4)); + } finally { + clob.free(); + } - fail(e.toString()); + NClob nclob; + nclob = cstmt.getObject(9, NClob.class); + try { + assertEquals("ntext", nclob.getSubString(1, 5)); + } finally { + nclob.free(); + } - } - try { - String out2 = cstmt.getObject("col2Value", String.class); - } - catch (Exception e) { + assertArrayEquals(new byte[] {0x63, (byte) 0xC3, 0x4D, 0x6B, (byte) 0xCA, (byte) 0xD5, 0x55, (byte) 0xEB, 0x64, (byte) 0xBF, 0x7E, (byte) 0x84, (byte) 0x8D, 0x02, (byte) 0xC3, 0x76}, + cstmt.getObject(10, byte[].class)); + assertEquals(java.sql.Date.valueOf("2017-05-19"), cstmt.getObject(11, java.sql.Date.class)); + assertEquals(java.sql.Date.valueOf("2017-05-19"), cstmt.getObject("col11Value", java.sql.Date.class)); - fail(e.toString()); - } + java.sql.Time expectedTime = new java.sql.Time(java.sql.Time.valueOf("10:47:15").getTime() + 123L); + assertEquals(expectedTime, cstmt.getObject(12, java.sql.Time.class)); + assertEquals(expectedTime, cstmt.getObject("col12Value", java.sql.Time.class)); - try { - Utils.dropProcedureIfExists(procName, stmt); - } - catch (Exception ex) { + assertEquals(java.sql.Timestamp.valueOf("2017-05-19 10:47:15.1234567"), cstmt.getObject(13, java.sql.Timestamp.class)); + assertEquals(java.sql.Timestamp.valueOf("2017-05-19 10:47:15.1234567"), cstmt.getObject("col13Value", java.sql.Timestamp.class)); + + assertEquals("2017-05-19 10:47:15.1234567 +02:00", cstmt.getObject(14, microsoft.sql.DateTimeOffset.class).toString()); + assertEquals("2017-05-19 10:47:15.1234567 +02:00", cstmt.getObject("col14Value", microsoft.sql.DateTimeOffset.class).toString()); + } finally { + Utils.dropProcedureIfExists(procName, stmt); + } } - ; - stmt.close(); - cstmt.close(); - conn.close(); } } diff --git a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java index 595c9fbfc7..e72ab2dc54 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java @@ -149,14 +149,14 @@ public static void invokeLogging() { Handler handler = null; String enableLogging = getConfiguredProperty("mssql_jdbc_logging", "false"); - - //If logging is not enable then return. - if(!"true".equalsIgnoreCase(enableLogging)) { + + // If logging is not enable then return. + if (!"true".equalsIgnoreCase(enableLogging)) { return; } String loggingHandler = getConfiguredProperty("mssql_jdbc_logging_handler", "not_configured"); - + try { // handler = new FileHandler("Driver.log"); if ("console".equalsIgnoreCase(loggingHandler)) { diff --git a/src/test/java/com/microsoft/sqlserver/testframework/DBResultSet.java b/src/test/java/com/microsoft/sqlserver/testframework/DBResultSet.java index 5e8a07025f..0851f39079 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/DBResultSet.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/DBResultSet.java @@ -233,18 +233,18 @@ public void verifydata(int ordinal, switch (metaData.getColumnType(ordinal + 1)) { case java.sql.Types.BIGINT: assertTrue((((Long) expectedData).longValue() == ((Long) retrieved).longValue()), - "Unexpected bigint value, expected: " + ((Long) expectedData).longValue() + " .Retrieved: " + ((Long) retrieved).longValue()); + "Unexpected bigint value, expected: " + (Long) expectedData + " .Retrieved: " + (Long) retrieved); break; case java.sql.Types.INTEGER: assertTrue((((Integer) expectedData).intValue() == ((Integer) retrieved).intValue()), "Unexpected int value, expected : " - + ((Integer) expectedData).intValue() + " ,received: " + ((Integer) retrieved).intValue()); + + (Integer) expectedData + " ,received: " + (Integer) retrieved); break; case java.sql.Types.SMALLINT: case java.sql.Types.TINYINT: assertTrue((((Short) expectedData).shortValue() == ((Short) retrieved).shortValue()), "Unexpected smallint/tinyint value, expected: " - + " " + ((Short) expectedData).shortValue() + " received: " + ((Short) retrieved).shortValue()); + + " " + (Short) expectedData + " received: " + (Short) retrieved); break; case java.sql.Types.BIT: @@ -253,7 +253,7 @@ public void verifydata(int ordinal, else expectedData = false; assertTrue((((Boolean) expectedData).booleanValue() == ((Boolean) retrieved).booleanValue()), "Unexpected bit value, expected: " - + ((Boolean) expectedData).booleanValue() + " ,received: " + ((Boolean) retrieved).booleanValue()); + + (Boolean) expectedData + " ,received: " + (Boolean) retrieved); break; case java.sql.Types.DECIMAL: @@ -264,12 +264,12 @@ public void verifydata(int ordinal, case java.sql.Types.DOUBLE: assertTrue((((Double) expectedData).doubleValue() == ((Double) retrieved).doubleValue()), "Unexpected float value, expected: " - + ((Double) expectedData).doubleValue() + " received: " + ((Double) retrieved).doubleValue()); + + (Double) expectedData + " received: " + (Double) retrieved); break; case java.sql.Types.REAL: assertTrue((((Float) expectedData).floatValue() == ((Float) retrieved).floatValue()), - "Unexpected real value, expected: " + ((Float) expectedData).floatValue() + " received: " + ((Float) retrieved).floatValue()); + "Unexpected real value, expected: " + (Float) expectedData + " received: " + (Float) retrieved); break; case java.sql.Types.VARCHAR: @@ -350,7 +350,7 @@ public Object getXXX(Object idx, } else if (idx instanceof Integer) { isInteger = true; - intOrdinal = ((Integer) idx).intValue(); + intOrdinal = (Integer) idx; } else { // Otherwise diff --git a/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlBigInt.java b/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlBigInt.java index e8fef48a8d..84126f5da5 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlBigInt.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlBigInt.java @@ -22,6 +22,6 @@ public SqlBigInt() { public Object createdata() { // TODO: include max value - return new Long(ThreadLocalRandom.current().nextLong(Long.MIN_VALUE, Long.MAX_VALUE)); + return ThreadLocalRandom.current().nextLong(Long.MIN_VALUE, Long.MAX_VALUE); } } \ No newline at end of file diff --git a/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlInt.java b/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlInt.java index e988fc49ed..2622f0745e 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlInt.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlInt.java @@ -21,6 +21,6 @@ public SqlInt() { public Object createdata() { // TODO: include max value - return new Integer(ThreadLocalRandom.current().nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE)); + return ThreadLocalRandom.current().nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE); } } \ No newline at end of file diff --git a/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlReal.java b/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlReal.java index bea1096960..5edaf59ac1 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlReal.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlReal.java @@ -20,6 +20,6 @@ public SqlReal() { @Override public Object createdata() { - return new Float(ThreadLocalRandom.current().nextDouble((Float) minvalue, (Float) maxvalue)); + return (float) ThreadLocalRandom.current().nextDouble((Float) minvalue, (Float) maxvalue); } } \ No newline at end of file diff --git a/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlSmallInt.java b/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlSmallInt.java index d821c9d7c0..7d2cebcae2 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlSmallInt.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlSmallInt.java @@ -21,6 +21,6 @@ public SqlSmallInt() { public Object createdata() { // TODO: include max value - return new Short((short) ThreadLocalRandom.current().nextInt(Short.MIN_VALUE, Short.MAX_VALUE)); + return (short) ThreadLocalRandom.current().nextInt(Short.MIN_VALUE, Short.MAX_VALUE); } } \ No newline at end of file diff --git a/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlTinyInt.java b/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlTinyInt.java index d8c7750dd8..ca5ca58fbc 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlTinyInt.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlTinyInt.java @@ -20,6 +20,6 @@ public SqlTinyInt() { public Object createdata() { // TODO: include max value - return new Short((short) ThreadLocalRandom.current().nextInt((short) minvalue, ((short) maxvalue))); + return (short) ThreadLocalRandom.current().nextInt((short) minvalue, ((short) maxvalue)); } } \ No newline at end of file diff --git a/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlTypeValue.java b/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlTypeValue.java index b1a7d94274..ae2b3345bd 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlTypeValue.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/sqlType/SqlTypeValue.java @@ -17,16 +17,16 @@ */ enum SqlTypeValue { // minValue // maxValue // nullValue - BIGINT (new Long(Long.MIN_VALUE), new Long(Long.MAX_VALUE), new Long(0)), - INTEGER (new Integer(Integer.MIN_VALUE), new Integer(Integer.MAX_VALUE), new Integer(0)), - SMALLINT (new Short(Short.MIN_VALUE), new Short(Short.MAX_VALUE), new Short((short) 0)), - TINYINT (new Short((short) 0), new Short((short) 255), new Short((short) 0)), + BIGINT (Long.MIN_VALUE, Long.MAX_VALUE, 0L), + INTEGER (Integer.MIN_VALUE, Integer.MAX_VALUE, 0), + SMALLINT (Short.MIN_VALUE, Short.MAX_VALUE, (short) 0), + TINYINT ((short) 0, (short) 255, (short) 0), BIT (0, 1, null), DECIMAL (new BigDecimal("-1.0E38").add(new BigDecimal("1")), new BigDecimal("1.0E38").subtract(new BigDecimal("1")), null), MONEY (new BigDecimal("-922337203685477.5808"), new BigDecimal("+922337203685477.5807"), null), SMALLMONEY (new BigDecimal("-214748.3648"), new BigDecimal("214748.3647"), null), - FLOAT (new Double(-1.79E308), new Double(+1.79E308), new Double(0)), - REAL (new Float(-3.4E38), new Float(+3.4E38), new Float(0)), + FLOAT (-1.79E308, +1.79E308, 0d), + REAL ((float) -3.4E38, (float) +3.4E38, 0f), CHAR (null, null, null),// CHAR used by char, nchar, varchar, nvarchar BINARY (null, null, null), DATETIME ("17530101T00:00:00.000", "99991231T23:59:59.997", null), @@ -34,7 +34,7 @@ enum SqlTypeValue { TIME ("00:00:00.0000000", "23:59:59.9999999", null), SMALLDATETIME ("19000101T00:00:00", "20790606T23:59:59", null), DATETIME2 ("00010101T00:00:00.0000000", "99991231T23:59:59.9999999", null), - DATETIMEOFFSET ("0001-01-01 00:00:00", "9999-12-31 23:59:59", null), + DATETIMEOFFSET ("0001-01-01 00:00:00", "9999-12-31 23:59:59", null), ; Object minValue; diff --git a/src/test/java/com/microsoft/sqlserver/testframework/util/RandomData.java b/src/test/java/com/microsoft/sqlserver/testframework/util/RandomData.java new file mode 100644 index 0000000000..139d3a7529 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/testframework/util/RandomData.java @@ -0,0 +1,798 @@ +package com.microsoft.sqlserver.testframework.util; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Date; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.Random; + +import microsoft.sql.DateTimeOffset; + +/** + * Utility class for generating random data for testing + */ +public class RandomData { + + private static Random r = new Random(); + + public static boolean returnNull = (0 == r.nextInt(5)); // 20% chance of return null + public static boolean returnFullLength = (0 == r.nextInt(2)); // 50% chance of return full length for char/nchar and binary types + public static boolean returnMinMax = (0 == r.nextInt(5)); // 20% chance of return Min/Max value + public static boolean returnZero = (0 == r.nextInt(10)); // 10% chance of return zero + + private static String specicalCharSet = "ÀÂÃÄËßîðÐ"; + private static String normalCharSet = "1234567890-=!@#$%^&*()_+qwertyuiop[]\\asdfghjkl;'zxcvbnm,./QWERTYUIOP{}|ASDFGHJKL:\"ZXCVBNM<>?"; + + private static String unicodeCharSet = "♠♣♥♦林花謝了春紅太匆匆無奈朝我附件为放假哇额外放我放问역사적으로본래한민족의영역은만주와연해주의일부를포함하였으나会和太空特工我來寒雨晚來風胭脂淚留人醉幾時重自是人生長恨水長東ྱོགས་སུ་འཁོར་བའི་ས་ཟླུུམ་ཞིག་ལ་ངོས་འཛིན་དགོས་ཏེ།ངག་ཕྱོαβγδεζηθικλμνξοπρστυφχψ太陽系の年齢もまた隕石の年代測定に依拠するので"; + + private static String numberCharSet = "1234567890"; + private static String numberCharSet2 = "123456789"; + + /** + * Utility method for generating a random boolean. + * + * @param nullable + * @return + */ + public static Boolean generateBoolean(boolean nullable) { + if (nullable) { + if (returnNull) { + return null; + } + } + + return r.nextBoolean(); + } + + /** + * Utility method for generating a random int. + * + * @param nullable + * @return + */ + public static Integer generateInt(boolean nullable) { + if (nullable) { + if (returnNull) { + return null; + } + } + + if (returnZero) { + return 0; + } + + if (returnMinMax) { + if (r.nextBoolean()) { + return 2147483647; + } + else { + return -2147483648; + } + } + + // can be either negative or positive + return r.nextInt(); + } + + /** + * Utility method for generating a random long. + * + * @param nullable + * @return + */ + public static Long generateLong(boolean nullable) { + if (nullable) { + if (returnNull) { + return null; + } + } + + if (returnZero) { + return 0L; + } + + if (returnMinMax) { + if (r.nextBoolean()) { + return 9223372036854775807L; + } + else { + return -9223372036854775808L; + } + } + + // can be either negative or positive + return r.nextLong(); + } + + /** + * Utility method for generating a random tinyint. + * + * @param nullable + * @return + */ + public static Short generateTinyint(boolean nullable) { + Integer value = pickInt(nullable, 255, 0); + + if (null != value) { + return value.shortValue(); + } + else { + return null; + } + } + + /** + * Utility method for generating a random short. + * + * @param nullable + * @return + */ + public static Short generateSmallint(boolean nullable) { + Integer value = pickInt(nullable, 32767, -32768); + + if (null != value) { + return value.shortValue(); + } + else { + return null; + } + } + + /** + * Utility method for generating a random BigDecimal. + * + * @param precision + * @param scale + * @param nullable + * @return + */ + public static BigDecimal generateDecimalNumeric(int precision, + int scale, + boolean nullable) { + + if (nullable) { + if (returnNull) { + return null; + } + } + + if (returnZero) { + return BigDecimal.ZERO.setScale(scale); + + } + + if (returnMinMax) { + BigInteger n; + if (r.nextBoolean()) { + n = BigInteger.TEN.pow(precision); + if (scale > 0) + return new BigDecimal(n, scale).subtract(new BigDecimal("" + Math.pow(10, -scale)).setScale(scale, BigDecimal.ROUND_HALF_UP)) + .negate(); + else + return new BigDecimal(n, scale).subtract(new BigDecimal("1")).negate(); + } + else { + n = BigInteger.TEN.pow(precision); + if (scale > 0) + return new BigDecimal(n, scale).subtract(new BigDecimal("" + Math.pow(10, -scale)).setScale(scale, BigDecimal.ROUND_HALF_UP)) + .negate(); + else + return new BigDecimal(n, scale).subtract(new BigDecimal("1")).negate(); + + } + + } + BigInteger n = BigInteger.TEN.pow(precision); + if (r.nextBoolean()) { + return new BigDecimal(newRandomBigInteger(n, r, precision), scale); + } + return (new BigDecimal(newRandomBigInteger(n, r, precision), scale).negate()); + + } + + /** + * Utility method for generating a random float. + * + * @param nullable + * @return + */ + public static Float generateReal(boolean nullable) { + Double doubleValue = generateFloat(24, nullable); + + if (null != doubleValue) { + return doubleValue.floatValue(); + } + else { + return null; + } + } + + /** + * Utility method for generating a random double. + * + * @param n + * integer + * @param nullable + * @return + */ + public static Double generateFloat(Integer n, + boolean nullable) { + if (nullable) { + if (returnNull) { + return null; + } + } + + if (returnZero) { + return new Double(0); + } + + // only 2 options: 24 or 53 + // The default value of n is 53. If 1<=n<=24, n is treated as 24. If 25<=n<=53, n is treated as 53. + // https://msdn.microsoft.com/en-us/library/ms173773.aspx + if (null == n) { + n = 53; + } + else if (25 <= n && 53 >= n) { + n = 53; + } + else { + n = 24; + } + + if (returnMinMax) { + if (53 == n) { + if (r.nextBoolean()) { + if (r.nextBoolean()) { + return Double.valueOf("1.79E+308"); + } + else { + return Double.valueOf("2.23E-308"); + } + } + else { + if (r.nextBoolean()) { + return Double.valueOf("-2.23E-308"); + } + else { + return Double.valueOf("-1.79E+308"); + } + } + } + else { + if (r.nextBoolean()) { + if (r.nextBoolean()) { + return Double.valueOf("3.40E+38"); + } + else { + return Double.valueOf("1.18E-38"); + } + } + else { + if (r.nextBoolean()) { + return Double.valueOf("-1.18E-38"); + } + else { + return Double.valueOf("-3.40E+38"); + } + } + } + } + + String intPart = "" + r.nextInt(10); + + // generate n bits of binary data and convert to long, then use the long as decimal part + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < n; i++) { + sb.append(r.nextInt(2)); + } + long longValue = Long.parseLong(sb.toString(), 2); + String stringValue = intPart + "." + longValue; + + return Double.valueOf(stringValue); + } + + /** + * Utility method for generating a random Money. + * + * @param nullable + * @return + */ + public static BigDecimal generateMoney(boolean nullable) { + String charSet = numberCharSet; + BigDecimal max = new BigDecimal("922337203685477.5807"); + BigDecimal min = new BigDecimal("-922337203685477.5808"); + float multiplier = 10000; + return generateMoneyOrSmallMoney(nullable, max, min, multiplier, charSet); + } + + /** + * Utility method for generating a random SmallMoney. + * + * @param nullable + * @return + */ + public static BigDecimal generateSmallMoney(boolean nullable) { + String charSet = numberCharSet; + BigDecimal max = new BigDecimal("214748.3647"); + BigDecimal min = new BigDecimal("-214748.3648"); + float multiplier = (float) (1.0 / 10000.0); + return generateMoneyOrSmallMoney(nullable, max, min, multiplier, charSet); + } + + /** + * Utility method for generating a random char or Nchar. + * + * @param columnLength + * @param nullable + * @param encrypted + * @return + */ + public static String generateCharTypes(String columnLength, + boolean nullable, + boolean encrypted) { + String charSet = normalCharSet; + + return buildCharOrNChar(columnLength, nullable, encrypted, charSet, 8001); + } + + public static String generateNCharTypes(String columnLength, + boolean nullable, + boolean encrypted) { + String charSet = specicalCharSet + normalCharSet + unicodeCharSet; + + return buildCharOrNChar(columnLength, nullable, encrypted, charSet, 4001); + } + + /** + * Utility method for generating a random binary. + * + * @param columnLength + * @param nullable + * @param encrypted + * @return + */ + public static byte[] generateBinaryTypes(String columnLength, + boolean nullable, + boolean encrypted) { + int maxBound = 8001; + + if (nullable) { + if (returnNull) { + return null; + } + } + + // if column is encrypted, string value cannot be "", not supported. + int minimumLength = 0; + if (encrypted) { + minimumLength = 1; + } + + int length; + if (columnLength.toLowerCase().equals("max")) { + // 50% chance of return value longer than 8000/4000 + if (r.nextBoolean()) { + length = r.nextInt(100000) + maxBound; + byte[] bytes = new byte[length]; + r.nextBytes(bytes); + return bytes; + } + else { + length = r.nextInt(maxBound - minimumLength) + minimumLength; + byte[] bytes = new byte[length]; + r.nextBytes(bytes); + return bytes; + } + } + else { + int columnLengthInt = Integer.parseInt(columnLength); + if (returnFullLength) { + length = columnLengthInt; + byte[] bytes = new byte[length]; + r.nextBytes(bytes); + return bytes; + } + else { + length = r.nextInt(columnLengthInt - minimumLength) + minimumLength; + byte[] bytes = new byte[length]; + r.nextBytes(bytes); + return bytes; + } + } + } + + /** + * Utility method for generating a random date. + * + * @param nullable + * @return + */ + public static Date generateDate(boolean nullable) { + if (nullable) { + if (returnNull) { + return null; + } + } + + long max = Timestamp.valueOf("9999-12-31 00:00:00.000").getTime(); + long min = Timestamp.valueOf("0001-01-01 00:00:00.000").getTime(); + + if (returnMinMax) { + if (r.nextBoolean()) { + return new Date(max); + } + else { + return new Date(min); + } + } + + while (true) { + long longValue = r.nextLong(); + + if (longValue >= min && longValue <= max) { + return new Date(longValue); + } + } + } + + /** + * Utility method for generating a random timestamp. + * + * @param nullable + * @return + */ + public static Timestamp generateDatetime(boolean nullable) { + long max = Timestamp.valueOf("9999-12-31 23:59:59.997").getTime(); + long min = Timestamp.valueOf("1753-01-01 00:00:00.000").getTime(); + + return generateTimestamp(nullable, max, min); + } + + /** + * Utility method for generating a random datetimeoffset. + * + * @param nullable + * @return + */ + public static DateTimeOffset generateDatetimeoffset(Integer precision, + boolean nullable) { + if (null == precision) { + precision = 7; + } + + DateTimeOffset maxDTS = calculateDateTimeOffsetMinMax("max", precision, "9999-12-31 23:59:59"); + DateTimeOffset minDTS = calculateDateTimeOffsetMinMax("min", precision, "0001-01-01 00:00:00"); + + long max = maxDTS.getTimestamp().getTime(); + long min = minDTS.getTimestamp().getTime(); + + Timestamp ts = generateTimestamp(nullable, max, min); + + if (null == ts) { + return null; + } + + if (returnMinMax) { + if (r.nextBoolean()) { + return maxDTS; + } + else { + // return minDTS; + return calculateDateTimeOffsetMinMax("min", precision, "0001-01-01 00:00:00.0000000"); + } + } + + int precisionDigits = buildPrecision(precision, numberCharSet2); + ts.setNanos(precisionDigits); + + int randomTimeZoneInMinutes = r.nextInt(1681) - 840; + + return microsoft.sql.DateTimeOffset.valueOf(ts, randomTimeZoneInMinutes); + } + + /** + * Utility method for generating a random small datetime. + * + * @param nullable + * @return + */ + public static Timestamp generateSmalldatetime(boolean nullable) { + long max = Timestamp.valueOf("2079-06-06 23:59:00").getTime(); + long min = Timestamp.valueOf("1900-01-01 00:00:00").getTime(); + + return generateTimestamp(nullable, max, min); + } + + /** + * Utility method for generating a random datetime. + * + * @param precision + * @param nullable + * @return + */ + public static Timestamp generateDatetime2(Integer precision, + boolean nullable) { + if (null == precision) { + precision = 7; + } + + long max = Timestamp.valueOf("9999-12-31 23:59:59").getTime(); + long min = Timestamp.valueOf("0001-01-01 00:00:00").getTime(); + + Timestamp ts = generateTimestamp(nullable, max, min); + + if (null == ts) { + return ts; + } + + if (returnMinMax) { + if (ts.getTime() == max) { + int precisionDigits = buildPrecision(precision, "9"); + ts.setNanos(precisionDigits); + return ts; + } + else { + ts.setNanos(0); + return ts; + } + } + + int precisionDigits = buildPrecision(precision, numberCharSet2); // not to use 0 in the random data for now. E.g creates 9040330 and when set + // it is 904033. + ts.setNanos(precisionDigits); + return ts; + } + + /** + * Utility method for generating a random time. + * + * @param precision + * @param nullable + * @return + */ + public static Time generateTime(Integer precision, + boolean nullable) { + if (null == precision) { + precision = 7; + } + + long max = Timestamp.valueOf("9999-12-31 23:59:59").getTime(); + long min = Timestamp.valueOf("0001-01-01 00:00:00").getTime(); + + Timestamp ts = generateTimestamp(nullable, max, min); + + if (null == ts) { + return null; + } + + if (returnMinMax) { + if (ts.getTime() == max) { + int precisionDigits = buildPrecision(precision, "9"); + ts.setNanos(precisionDigits); + return new Time(ts.getTime()); + } + else { + ts.setNanos(0); + return new Time(ts.getTime()); + } + } + + int precisionDigits = buildPrecision(precision, numberCharSet); + ts.setNanos(precisionDigits); + return new Time(ts.getTime()); + } + + private static int buildPrecision(int precision, + String charSet) { + String stringValue = calculatePrecisionDigits(precision, charSet); + return Integer.parseInt(stringValue); + } + + private static String calculatePrecisionDigits(int precision, + String charSet) { + // setNanos(999999900) gives 00:00:00.9999999 + // so, this value has to be 9 digits + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < precision; i++) { + char c = pickRandomChar(charSet); + sb.append(c); + } + + for (int i = sb.length(); i < 9; i++) { + sb.append("0"); + } + + return sb.toString(); + } + + private static Timestamp generateTimestamp(boolean nullable, + long max, + long min) { + if (nullable) { + if (returnNull) { + return null; + } + } + + if (returnMinMax) { + if (r.nextBoolean()) { + return new Timestamp(max); + } + else { + return new Timestamp(min); + } + } + + while (true) { + long longValue = r.nextLong(); + + if (longValue >= min && longValue <= max) { + return new Timestamp(longValue); + } + } + } + + private static BigDecimal generateMoneyOrSmallMoney(boolean nullable, + BigDecimal max, + BigDecimal min, + float multiplier, + String charSet) { + if (nullable) { + if (returnNull) { + return null; + } + } + + if (returnZero) { + return BigDecimal.ZERO.setScale(4); + } + + if (returnMinMax) { + if (r.nextBoolean()) { + return max; + } + else { + return min; + } + } + + long intPart = (long) (r.nextInt() * multiplier); + + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < 4; i++) { + char c = pickRandomChar(charSet); + sb.append(c); + } + + return new BigDecimal(intPart + "." + sb.toString()); + } + + private static DateTimeOffset calculateDateTimeOffsetMinMax(String maxOrMin, + Integer precision, + String tsMinMax) { + int providedTimeZoneInMinutes; + if (maxOrMin.toLowerCase().equals("max")) { + providedTimeZoneInMinutes = 840; + } + else { + providedTimeZoneInMinutes = -840; + } + + Timestamp tsMax = Timestamp.valueOf(tsMinMax); + + Calendar cal = Calendar.getInstance(); + long offset = cal.get(Calendar.ZONE_OFFSET); // in milliseconds + + // max Timestamp + difference of current time zone and GMT - provided time zone in milliseconds + tsMax = new Timestamp(tsMax.getTime() + offset - (providedTimeZoneInMinutes * 60 * 1000)); + + if (maxOrMin.toLowerCase().equals("max")) { + int precisionDigits = buildPrecision(precision, "9"); + tsMax.setNanos(precisionDigits); + } + + return microsoft.sql.DateTimeOffset.valueOf(tsMax, providedTimeZoneInMinutes); + } + + private static Integer pickInt(boolean nullable, + int max, + int min) { + if (nullable) { + if (returnNull) { + return null; + } + } + + if (returnZero) { + return 0; + } + + if (returnMinMax) { + if (r.nextBoolean()) { + return max; + } + else { + return min; + } + } + + return (int) r.nextInt(max - min) + min; + } + + private static String buildCharOrNChar(String columnLength, + boolean nullable, + boolean encrypted, + String charSet, + int maxBound) { + + if (nullable) { + if (returnNull) { + return null; + } + } + + // if column is encrypted, string value cannot be "", not supported. + int minimumLength = 0; + if (encrypted) { + minimumLength = 1; + } + + int length; + if (columnLength.toLowerCase().equals("max")) { + // 50% chance of return value longer than 8000/4000 + if (r.nextBoolean()) { + length = r.nextInt(100000) + maxBound; + return buildRandomString(length, charSet); + } + else { + length = r.nextInt(maxBound - minimumLength) + minimumLength; + return buildRandomString(length, charSet); + } + } + else { + int columnLengthInt = Integer.parseInt(columnLength); + if (returnFullLength) { + length = columnLengthInt; + return buildRandomString(length, charSet); + } + else { + length = r.nextInt(columnLengthInt - minimumLength) + minimumLength; + return buildRandomString(length, charSet); + } + } + } + + private static String buildRandomString(int length, + String charSet) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < length; i++) { + char c = pickRandomChar(charSet); + sb.append(c); + } + + return sb.toString(); + } + + private static char pickRandomChar(String charSet) { + int charSetLength = charSet.length(); + + int randomIndex = r.nextInt(charSetLength); + return charSet.charAt(randomIndex); + } + + private static BigInteger newRandomBigInteger(BigInteger n, + Random rnd, + int precision) { + BigInteger r; + do { + r = new BigInteger(n.bitLength(), rnd); + } + while (r.toString().length() != precision); + + return r; + } +} diff --git a/src/test/java/com/microsoft/sqlserver/testframework/util/Util.java b/src/test/java/com/microsoft/sqlserver/testframework/util/Util.java new file mode 100644 index 0000000000..f1e5167c4f --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/testframework/util/Util.java @@ -0,0 +1,292 @@ +package com.microsoft.sqlserver.testframework.util; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Timestamp; +import java.util.Calendar; + +import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.SQLServerDatabaseMetaData; +import com.microsoft.sqlserver.jdbc.SQLServerStatementColumnEncryptionSetting; + +/** + * Utility class for testing + */ +public class Util { + + /** + * Utility method for generating a prepared statement + * + * @param connection + * connection object + * @param sql + * SQL string + * @param stmtColEncSetting + * SQLServerStatementColumnEncryptionSetting object + * @return + */ + public static PreparedStatement getPreparedStmt(Connection connection, + String sql, + SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLException { + if (null == stmtColEncSetting) { + return ((SQLServerConnection) connection).prepareStatement(sql); + } + else { + return ((SQLServerConnection) connection).prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, + connection.getHoldability(), stmtColEncSetting); + } + } + + /** + * Utility method for a statement + * + * @param connection + * connection object + * @param sql + * SQL string + * @param stmtColEncSetting + * SQLServerStatementColumnEncryptionSetting object + * @return + */ + public static Statement getStatement(Connection connection, + SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLException { + // default getStatement assumes resultSet is type_forward_only and concur_read_only + if (null == stmtColEncSetting) { + return ((SQLServerConnection) connection).createStatement(); + } + else { + return ((SQLServerConnection) connection).createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, + connection.getHoldability(), stmtColEncSetting); + } + } + + /** + * Utility method for a scrollable statement + * + * @param connection + * connection object + * @return + */ + public static Statement getScrollableStatement(Connection connection) throws SQLException { + return ((SQLServerConnection) connection).createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + } + + /** + * Utility method for a scrollable statement + * + * @param connection + * connection object + * @param stmtColEncSetting + * SQLServerStatementColumnEncryptionSetting object + * @return + */ + public static Statement getScrollableStatement(Connection connection, + SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLException { + return ((SQLServerConnection) connection).createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.TYPE_SCROLL_SENSITIVE, + ResultSet.CONCUR_UPDATABLE, stmtColEncSetting); + } + + /** + * Utility method for a statement + * + * @param connection + * connection object + * @param stmtColEncSetting + * SQLServerStatementColumnEncryptionSetting object + * @param rsScrollSensitivity + * @param rsConcurrence + * @return + */ + public static Statement getStatement(Connection connection, + SQLServerStatementColumnEncryptionSetting stmtColEncSetting, + int rsScrollSensitivity, + int rsConcurrence) throws SQLException { + // overloaded getStatement allows setting resultSet type + if (null == stmtColEncSetting) { + return ((SQLServerConnection) connection).createStatement(rsScrollSensitivity, rsConcurrence, connection.getHoldability()); + } + else { + return ((SQLServerConnection) connection).createStatement(rsScrollSensitivity, rsConcurrence, connection.getHoldability(), + stmtColEncSetting); + } + } + + /** + * Utility method for a callable statement + * + * @param connection + * connection object + * @param stmtColEncSetting + * SQLServerStatementColumnEncryptionSetting object + * @param sql + * @return + */ + public static CallableStatement getCallableStmt(Connection connection, + String sql, + SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLException { + if (null == stmtColEncSetting) { + return ((SQLServerConnection) connection).prepareCall(sql); + } + else { + return ((SQLServerConnection) connection).prepareCall(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, + connection.getHoldability(), stmtColEncSetting); + } + } + + /** + * Utility method for a datetime value + * + * @param value + * @return + */ + public static Object roundSmallDateTimeValue(Object value) { + if (value == null) { + return null; + } + + Calendar cal; + java.sql.Timestamp ts = null; + int nanos = -1; + + if (value instanceof Calendar) { + cal = (Calendar) value; + } + else { + ts = (java.sql.Timestamp) value; + cal = Calendar.getInstance(); + cal.setTimeInMillis(ts.getTime()); + nanos = ts.getNanos(); + } + + // round to the nearest minute + double seconds = cal.get(Calendar.SECOND) + (nanos == -1 ? ((double) cal.get(Calendar.MILLISECOND) / 1000) : ((double) nanos / 1000000000)); + if (seconds > 29.998) { + cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE) + 1); + } + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + nanos = 0; + + // required to force computation + cal.getTimeInMillis(); + + // return appropriate value + if (value instanceof Calendar) { + return cal; + } + else { + ts.setTime(cal.getTimeInMillis()); + ts.setNanos(nanos); + return ts; + } + } + + /** + * Utility method for a datetime value + * + * @param value + * @return + */ + public static Object roundDatetimeValue(Object value) { + if (value == null) + return null; + Timestamp ts = value instanceof Timestamp ? (Timestamp) value : new Timestamp(((Calendar) value).getTimeInMillis()); + int millis = ts.getNanos() / 1000000; + int lastDigit = (int) (millis % 10); + switch (lastDigit) { + // 0, 1 -> 0 + case 1: + ts.setNanos((millis - 1) * 1000000); + break; + + // 2, 3, 4 -> 3 + case 2: + ts.setNanos((millis + 1) * 1000000); + break; + case 4: + ts.setNanos((millis - 1) * 1000000); + break; + + // 5, 6, 7, 8 -> 7 + case 5: + ts.setNanos((millis + 2) * 1000000); + break; + case 6: + ts.setNanos((millis + 1) * 1000000); + break; + case 8: + ts.setNanos((millis - 1) * 1000000); + break; + + // 9 -> 0 with overflow + case 9: + ts.setNanos(0); + ts.setTime(ts.getTime() + millis + 1); + break; + + // default, i.e. 0, 3, 7 -> 0, 3, 7 + // don't change the millis but make sure that any + // sub-millisecond digits are zeroed out + default: + ts.setNanos((millis) * 1000000); + } + if (value instanceof Calendar) { + ((Calendar) value).setTimeInMillis(ts.getTime()); + ((Calendar) value).getTimeInMillis(); + return value; + } + return ts; + } + + /** + * Utility function for safely closing open resultset/statement/connection + * + * @param ResultSet + * @param Statement + * @param Connection + */ + public static void close(ResultSet rs, + Statement stmt, + Connection con) { + if (rs != null) { + try { + rs.close(); + + } + catch (SQLException e) { + System.out.println("The result set cannot be closed."); + } + } + if (stmt != null) { + try { + stmt.close(); + } + catch (SQLException e) { + System.out.println("The statement cannot be closed."); + } + } + if (con != null) { + try { + con.close(); + } + catch (SQLException e) { + System.out.println("The data source connection cannot be closed."); + } + } + } + + /** + * Utility function for checking if the system supports JDBC 4.2 + * + * @param con + * @return + */ + public static boolean supportJDBC42(Connection con) throws SQLException { + SQLServerDatabaseMetaData meta = (SQLServerDatabaseMetaData) con.getMetaData(); + return (meta.getJDBCMajorVersion() >= 4 && meta.getJDBCMinorVersion() >= 2); + } +}