From a0a0811dfe82cef446144275befd4723019773e6 Mon Sep 17 00:00:00 2001 From: v-afrafi Date: Thu, 26 Jan 2017 15:26:36 -0800 Subject: [PATCH 01/13] first commit of changes adding to Datatypes --- src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java | 6 ++++-- src/main/java/microsoft/sql/Types.java | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java index d8db30ec73..69746642fa 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java @@ -845,7 +845,8 @@ 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"), + Variant (Category.Variant, microsoft.sql.Types.VARIANT, "java.lang.Object"); final Category category; private final int intValue; @@ -892,7 +893,8 @@ enum Category { SQLXML, UNKNOWN, TVP, - GUID; + GUID, + Variant; } // This SetterConversion enum is based on the Category enum diff --git a/src/main/java/microsoft/sql/Types.java b/src/main/java/microsoft/sql/Types.java index 0068fda086..f517c0b169 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; + + /** + * + */ + public static final int VARIANT = -156; } From 0b55e89b831d51d5a0a8fb52c347b8af7ef4b3bb Mon Sep 17 00:00:00 2001 From: v-afrafi Date: Mon, 30 Jan 2017 13:11:13 -0800 Subject: [PATCH 02/13] updates --- .../microsoft/sqlserver/jdbc/IOBuffer.java | 16 +++++ .../com/microsoft/sqlserver/jdbc/dtv.java | 69 +++++++++++-------- 2 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 5eec6f86d1..0cb6a02fc5 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -6424,6 +6424,18 @@ final void readBytes(byte[] value, payloadOffset += bytesToCopy; } } + + final int readSqlVariant( ) throws SQLServerException{ + + if (payloadOffset + 4 <= currentPacket.payloadLength) { + payloadOffset += 4; + int value = readUnsignedByte(); + return value; + } + return 0; + + + } final byte[] readWrappedBytes(int valueLength) throws SQLServerException { assert valueLength <= valueBytes.length; @@ -6444,6 +6456,10 @@ final Object readDecimal(int valueLength, return DDC.convertBigDecimalToObject(Util.readBigDecimal(valueBytes, valueLength, typeInfo.getScale()), jdbcType, streamType); } +// final Object readSqlVariant() { +// +// } + final Object readMoney(int valueLength, JDBCType jdbcType, StreamType streamType) throws SQLServerException { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java index cee390b2ef..7f1a2da077 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java @@ -3014,37 +3014,48 @@ 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); + + int type = tdsReader.readSqlVariant();//tdsReader.readUnsignedByte(); + int cbPropsActual = tdsReader.readUnsignedByte(); +// System.out.println(TDSType.valueOf(type)); + switch(TDSType.valueOf(type)){ + case INT4: + INTEGER.build(typeInfo, tdsReader); + break; } - 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(); + } - } +// 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(); +// } +// } }); private final TDSType tdsType; From 9a7415a7ad7ee2ae6b6b01b8819490dcd216074f Mon Sep 17 00:00:00 2001 From: v-afrafi Date: Wed, 1 Feb 2017 10:34:17 -0800 Subject: [PATCH 03/13] added int support --- .../microsoft/sqlserver/jdbc/DataTypes.java | 8 +++- .../sqlserver/jdbc/PLPInputStream.java | 27 ++++++------ .../com/microsoft/sqlserver/jdbc/dtv.java | 43 +++++++++++++++---- 3 files changed, 56 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java index 69746642fa..22cea440d0 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java @@ -358,7 +358,13 @@ enum GetterConversion SSType.Category.GUID, EnumSet.of( JDBCType.Category.BINARY, - JDBCType.Category.CHARACTER)); + JDBCType.Category.CHARACTER)), + VARIANT ( + SSType.Category.VARIANT, + EnumSet.of( + JDBCType.Category.NUMERIC, + JDBCType.Category.CHARACTER, + JDBCType.Category.BINARY)); private final SSType.Category from; private final EnumSet to; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/PLPInputStream.java b/src/main/java/com/microsoft/sqlserver/jdbc/PLPInputStream.java index ab3679ef09..1b8b41facd 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/PLPInputStream.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/PLPInputStream.java @@ -43,20 +43,23 @@ class PLPInputStream extends BaseInputStream { final static boolean isNull(TDSReader tdsReader) throws SQLServerException { TDSReaderMark mark = tdsReader.mark(); try { - PLPInputStream tempPLP = PLPInputStream.makeTempStream(tdsReader, false, null); - try { - if (null != tempPLP) { - tempPLP.close(); - return false; - } - } - catch (IOException e) { - tdsReader.getConnection().terminate(SQLServerException.DRIVER_ERROR_IO_FAILED, e.getMessage()); - } - return true; +// +// } +// PLPInputStream tempPLP = PLPInputStream.makeTempStream(tdsReader, false, null); + return null == PLPInputStream.makeTempStream(tdsReader, false, null); +// try { +// if (null != tempPLP) { +// tempPLP.close(); +// return false; +// } +// } +// catch (IOException e) { +// tdsReader.getConnection().terminate(SQLServerException.DRIVER_ERROR_IO_FAILED, e.getMessage()); +// } +// return true; } finally { - if (null != tdsReader) +// if (null != tdsReader) tdsReader.reset(mark); } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java index 7f1a2da077..48a2c63f0f 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java @@ -3014,15 +3014,27 @@ public void apply(TypeInfo typeInfo, */ public void apply(TypeInfo typeInfo, TDSReader tdsReader) throws SQLServerException { - - int type = tdsReader.readSqlVariant();//tdsReader.readUnsignedByte(); - int cbPropsActual = tdsReader.readUnsignedByte(); -// System.out.println(TDSType.valueOf(type)); - switch(TDSType.valueOf(type)){ - case INT4: - INTEGER.build(typeInfo, tdsReader); - break; - } + typeInfo.ssLenType = SSLenType.LONGLENTYPE; + typeInfo.maxLength = tdsReader.readInt(); + typeInfo.ssType = SSType.SQL_VARIANT; +// typeInfo.precision = 255; +// typeInfo.scale = 255; +// switch (TDSType.valueOf(tdsReader.readSqlVariant())) +// { +// case INTN: +// break; +// case INT4: +// break; +// } + +// int type = tdsReader.readSqlVariant();//tdsReader.readUnsignedByte(); +// int cbPropsActual = tdsReader.readUnsignedByte(); +//// System.out.println(TDSType.valueOf(type)); +// switch(TDSType.valueOf(type)){ +// case INT4: +// INTEGER.build(typeInfo, tdsReader); +// break; +// } } // try { @@ -3508,10 +3520,14 @@ private void getValuePrep(TypeInfo typeInfo, valueLength = tdsReader.readInt(); } } + else { valueLength = tdsReader.readInt(); isNull = (0 == valueLength); } +// if (SSType.SQL_VARIANT == typeInfo.getSSType()){ +// typeInfo.ssType = SSType.SQL_VARIANT; +// } break; } @@ -3950,6 +3966,15 @@ Object getValue(DTV dtv, case GUID: convertedValue = tdsReader.readGUID(valueLength, jdbcType, streamGetterArgs.streamType); break; + + case SQL_VARIANT: + int type = tdsReader.readUnsignedByte(); + switch(TDSType.valueOf(type)){ + case INT4: + int vprop = tdsReader.readUnsignedByte(); + convertedValue = DDC.convertIntegerToObject(tdsReader.readInt(), valueLength, jdbcType, streamGetterArgs.streamType); + break; + } // Unknown SSType should have already been rejected by TypeInfo.setFromTDS() default: From 7c584b116090eb753f9931cf09aa43c3ef17b43f Mon Sep 17 00:00:00 2001 From: v-afrafi Date: Mon, 6 Feb 2017 10:38:10 -0800 Subject: [PATCH 04/13] sql_variant update for bigDecimal support --- .../microsoft/sqlserver/jdbc/DataTypes.java | 8 +++++-- .../sqlserver/jdbc/SQLServerBulkCopy.java | 2 ++ .../com/microsoft/sqlserver/jdbc/dtv.java | 24 ++++++++++++++++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java index 22cea440d0..d825660011 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java @@ -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.VARIANT, "sql_variant", JDBCType.Variant), UDT (Category.UDT, "udt", JDBCType.VARBINARY), XML (Category.XML, "xml", JDBCType.LONGNVARCHAR), TIMESTAMP (Category.TIMESTAMP, "timestamp", JDBCType.BINARY); @@ -364,7 +364,11 @@ enum GetterConversion EnumSet.of( JDBCType.Category.NUMERIC, JDBCType.Category.CHARACTER, - JDBCType.Category.BINARY)); + JDBCType.Category.BINARY, + JDBCType.Category.CHARACTER, + JDBCType.Category.NCHARACTER, + JDBCType.Category.LONG_CHARACTER, + JDBCType.Category.LONG_NCHARACTER)); private final SSType.Category from; private final EnumSet to; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java index 0a8adae96f..d771333464 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java @@ -1438,6 +1438,8 @@ private String getDestTypeFromSrcType(int srcColIndx, else { return "datetimeoffset(" + bulkScale + ")"; } + case microsoft.sql.Types.VARIANT: + return "sql_variant"; default: { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_BulkTypeNotSupported")); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java index 48a2c63f0f..e655c460d7 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java @@ -2480,6 +2480,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. @@ -3969,11 +3973,29 @@ Object getValue(DTV dtv, case SQL_VARIANT: int type = tdsReader.readUnsignedByte(); + int cbPropsActual = tdsReader.readUnsignedByte(); switch(TDSType.valueOf(type)){ + case INT8: + convertedValue = DDC.convertLongToObject(tdsReader.readLong(), jdbcType, baseSSType, streamGetterArgs.streamType); + break; case INT4: - int vprop = tdsReader.readUnsignedByte(); convertedValue = DDC.convertIntegerToObject(tdsReader.readInt(), valueLength, jdbcType, streamGetterArgs.streamType); break; + case INT2: + convertedValue = DDC.convertIntegerToObject(tdsReader.readShort(), valueLength, jdbcType, streamGetterArgs.streamType); + break; + case INT1: + convertedValue = DDC.convertIntegerToObject(tdsReader.readUnsignedByte(), valueLength, jdbcType, + streamGetterArgs.streamType); + break; + case DECIMALN: + int precision = tdsReader.readUnsignedByte(); + typeInfo.setScale( tdsReader.readUnsignedByte() ); + int lengthTotal = valueLength; + int lengthConsumed = 2 + cbPropsActual; + int tempvalueLength = lengthTotal - lengthConsumed; + convertedValue = tdsReader.readDecimal(tempvalueLength, typeInfo, jdbcType, streamGetterArgs.streamType); + break; } // Unknown SSType should have already been rejected by TypeInfo.setFromTDS() From 66713c8a08aa497c25aecab2431dd154a24d35d8 Mon Sep 17 00:00:00 2001 From: v-afrafi Date: Tue, 7 Feb 2017 15:52:47 -0800 Subject: [PATCH 05/13] for storedprocedure --- .../java/com/microsoft/sqlserver/jdbc/Parameter.java | 4 ++++ src/main/java/com/microsoft/sqlserver/jdbc/dtv.java | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java index 6c12683c43..25136fe475 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java @@ -883,7 +883,11 @@ else if ((null != jdbcTypeSetByUser) && ((jdbcTypeSetByUser == JDBCType.NVARCHAR case GUID: param.typeDefinition = SSType.GUID.toString(); break; + case Variant: + // param.typeDefinition = SSType.INTEGER.toString(); + param.typeDefinition = SSType.SQL_VARIANT.toString(); + break; default: assert false : "Unexpected JDBC type " + dtv.getJdbcType(); break; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java index e655c460d7..e622ce350d 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java @@ -43,6 +43,8 @@ import com.microsoft.sqlserver.jdbc.JavaType.SetterConversionAE; +import microsoft.sql.SqlVariant; + /** * Defines an abstraction for execution of type-specific operations on DTV values. * @@ -139,6 +141,8 @@ abstract void execute(DTV dtv, abstract void execute(DTV dtv, TVP tvpValue) throws SQLServerException; + abstract void execute(DTV dtv, + SqlVariant SqlVariantValue) throws SQLServerException; } /** @@ -1580,7 +1584,9 @@ final void executeOp(DTVExecuteOp op) throws SQLServerException { case STRUCT: unsupportedConversion = true; break; - + case Variant: + op.execute(this, (microsoft.sql.SqlVariant) null); + break; case UNKNOWN: default: assert false : "Unexpected JDBCType: " + jdbcType; @@ -1857,6 +1863,7 @@ else if ((JDBCType.VARCHAR == jdbcTypeSetByUser) || (JDBCType.CHAR == jdbcTypeSe case SQLXML: op.execute(this, (SQLServerSQLXML) value); break; + default: assert false : "Unexpected JavaType: " + javaType; From bbea94d4322220f837d50fce62a73ee02d638fb1 Mon Sep 17 00:00:00 2001 From: v-afrafi Date: Wed, 8 Feb 2017 11:33:41 -0800 Subject: [PATCH 06/13] added sql variant in microsoft.Types --- .../microsoft/sqlserver/jdbc/Parameter.java | 12 +++++++++++ .../com/microsoft/sqlserver/jdbc/dtv.java | 20 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java index 25136fe475..4482f094dc 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java @@ -26,6 +26,8 @@ import java.util.Calendar; import java.util.Locale; +import microsoft.sql.SqlVariant; + /** * 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. @@ -1142,6 +1144,16 @@ 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 { + // TODO Auto-generated method stub + + } + } /** diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java index e622ce350d..14a574dff4 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java @@ -1440,6 +1440,16 @@ 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 { + // TODO Auto-generated method stub + + } } /** @@ -2291,6 +2301,16 @@ 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 { + // TODO Auto-generated method stub + + } } void setValue(DTV dtv, From fcf60a3732424f45e0609c096c8b3cf7f9817cde Mon Sep 17 00:00:00 2001 From: v-afrafi Date: Wed, 22 Mar 2017 10:00:56 -0700 Subject: [PATCH 07/13] added temporal types and string type support for sql variant --- .../microsoft/sqlserver/jdbc/DataTypes.java | 5 +- .../com/microsoft/sqlserver/jdbc/dtv.java | 141 ++++++++++++++++-- .../jdbc/datatypes/SQLVariantTest.java | 104 +++++++++++++ 3 files changed, 237 insertions(+), 13 deletions(-) create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantTest.java diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java index d825660011..f9d6c1117f 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java @@ -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.Variant), + SQL_VARIANT (Category.VARIANT, "sql_variant", JDBCType.CHAR), //TODO: was variant UDT (Category.UDT, "udt", JDBCType.VARBINARY), XML (Category.XML, "xml", JDBCType.LONGNVARCHAR), TIMESTAMP (Category.TIMESTAMP, "timestamp", JDBCType.BINARY); @@ -368,7 +368,8 @@ enum GetterConversion JDBCType.Category.CHARACTER, JDBCType.Category.NCHARACTER, JDBCType.Category.LONG_CHARACTER, - JDBCType.Category.LONG_NCHARACTER)); + JDBCType.Category.LONG_NCHARACTER, + JDBCType.Category.Variant)); private final SSType.Category from; private final EnumSet to; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java index 14a574dff4..50faf5e5cc 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java @@ -41,6 +41,7 @@ import java.util.TimeZone; import java.util.UUID; +import com.microsoft.sqlserver.jdbc.JDBCType.Category; import com.microsoft.sqlserver.jdbc.JavaType.SetterConversionAE; import microsoft.sql.SqlVariant; @@ -2417,7 +2418,7 @@ final class TypeInfo { private int displaySize;// size is in characters. display size assumes a formatted hexa decimal representation for binaries. private int scale; private short flags; - private SSType ssType; + public SSType ssType; private int userType; private String udtTypeName; @@ -2432,6 +2433,10 @@ SSType getSSType() { SSLenType getSSLenType() { return ssLenType; } + + void setSSLenType(SSLenType ssLenType){ + this.ssLenType = ssLenType; + } String getSSTypeName() { return (SSType.UDT == ssType) ? udtTypeName : ssType.toString(); @@ -2440,14 +2445,24 @@ 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; @@ -2464,6 +2479,10 @@ void setSQLCollation(SQLCollation collation) { Charset getCharset() { return charset; } + + void setCharset(Charset charset){ + this.charset = charset; + } boolean isNullable() { return 0x0001 == (flags & 0x0001); @@ -3045,7 +3064,7 @@ public void apply(TypeInfo typeInfo, */ public void apply(TypeInfo typeInfo, TDSReader tdsReader) throws SQLServerException { - typeInfo.ssLenType = SSLenType.LONGLENTYPE; + typeInfo.ssLenType = SSLenType.LONGLENTYPE; //Variant type should be LONGLENTYPE length. typeInfo.maxLength = tdsReader.readInt(); typeInfo.ssType = SSType.SQL_VARIANT; // typeInfo.precision = 255; @@ -3553,12 +3572,12 @@ private void getValuePrep(TypeInfo typeInfo, } else { - valueLength = tdsReader.readInt(); + valueLength = tdsReader.readInt(); isNull = (0 == valueLength); } -// if (SSType.SQL_VARIANT == typeInfo.getSSType()){ -// typeInfo.ssType = SSType.SQL_VARIANT; -// } + if (SSType.SQL_VARIANT == typeInfo.getSSType()){ + typeInfo.ssType = SSType.SQL_VARIANT; + } break; } @@ -3998,10 +4017,10 @@ Object getValue(DTV dtv, convertedValue = tdsReader.readGUID(valueLength, jdbcType, streamGetterArgs.streamType); break; - case SQL_VARIANT: - int type = tdsReader.readUnsignedByte(); + case SQL_VARIANT: + int baseType = tdsReader.readUnsignedByte(); int cbPropsActual = tdsReader.readUnsignedByte(); - switch(TDSType.valueOf(type)){ + switch(TDSType.valueOf(baseType)){ case INT8: convertedValue = DDC.convertLongToObject(tdsReader.readLong(), jdbcType, baseSSType, streamGetterArgs.streamType); break; @@ -4016,6 +4035,7 @@ Object getValue(DTV dtv, streamGetterArgs.streamType); break; case DECIMALN: + case NUMERICN: int precision = tdsReader.readUnsignedByte(); typeInfo.setScale( tdsReader.readUnsignedByte() ); int lengthTotal = valueLength; @@ -4023,8 +4043,107 @@ Object getValue(DTV dtv, int tempvalueLength = lengthTotal - lengthConsumed; convertedValue = tdsReader.readDecimal(tempvalueLength, typeInfo, jdbcType, streamGetterArgs.streamType); break; - } + + case MONEY4: + lengthTotal = valueLength; + lengthConsumed = 2 + cbPropsActual; + tempvalueLength = lengthTotal - lengthConsumed; + typeInfo.ssType = SSType.SMALLMONEY; + typeInfo.setMaxLength(4); + typeInfo.setPrecision(Long.toString(Long.MAX_VALUE).length()); + typeInfo.setDisplaySize( ("-" + "." + Integer.toString(Integer.MAX_VALUE)).length()); + typeInfo.setScale(4); + + convertedValue = tdsReader.readMoney(tempvalueLength, jdbcType, streamGetterArgs.streamType); + break; + case MONEY8: + lengthTotal = valueLength; + lengthConsumed = 2 + cbPropsActual; + tempvalueLength = lengthTotal - lengthConsumed; + typeInfo.ssType = SSType.MONEY; + typeInfo.setMaxLength(8); + typeInfo.setPrecision(Long.toString(Long.MAX_VALUE).length()); + typeInfo.setDisplaySize( ("-" + "." + Integer.toString(Integer.MAX_VALUE)).length()); + typeInfo.setScale(4); + + convertedValue = tdsReader.readMoney(tempvalueLength, jdbcType, streamGetterArgs.streamType); + break; + case BIGVARCHAR: + lengthTotal = valueLength; + lengthConsumed = 2 + cbPropsActual; + tempvalueLength = lengthTotal - lengthConsumed; + + SQLCollation collation = null; + collation = tdsReader.readCollation(); + typeInfo.setSQLCollation(collation); + + + typeInfo.setSSLenType(SSLenType.USHORTLENTYPE); + int maxLength = tdsReader.readUnsignedShort(); + typeInfo.setMaxLength(maxLength); + if (maxLength > DataTypes.SHORT_VARTYPE_MAX_BYTES) + tdsReader.throwInvalidTDS(); + typeInfo.setDisplaySize(maxLength); + typeInfo.setPrecision(maxLength); + +// typeInfo.ssType = SSType.CHAR; + typeInfo.setCharset(collation.getCharset()); + + convertedValue = DDC.convertStreamToObject(new SimpleInputStream(tdsReader, tempvalueLength, streamGetterArgs, this), typeInfo, + jdbcType, streamGetterArgs); + break; + + case DATETIME8: + case DATETIME4: +// typeInfo.setScale( 3); + lengthTotal = valueLength; + lengthConsumed = 2 + cbPropsActual; + tempvalueLength = lengthTotal - lengthConsumed; + convertedValue = tdsReader.readDateTime(tempvalueLength, cal, jdbcType, streamGetterArgs.streamType); + break; + case DATEN: + lengthTotal = valueLength; + lengthConsumed = 2 + cbPropsActual; + tempvalueLength = lengthTotal - lengthConsumed; + convertedValue = tdsReader.readDate(tempvalueLength, cal, jdbcType); + break; + case TIMEN: + lengthTotal = valueLength; + typeInfo.setScale(tdsReader.readUnsignedByte()); + lengthConsumed = 2 + cbPropsActual; + tempvalueLength = lengthTotal - lengthConsumed; + convertedValue = tdsReader.readTime(tempvalueLength, typeInfo, cal, jdbcType); + break; + case DATETIME2N: + lengthTotal = valueLength; + typeInfo.setScale(tdsReader.readUnsignedByte()); + lengthConsumed = 2 + cbPropsActual; + tempvalueLength = lengthTotal - lengthConsumed; + convertedValue = tdsReader.readDateTime2(tempvalueLength, typeInfo, cal, jdbcType); + break; + case BIGBINARY: + case BIGVARBINARY: + lengthTotal = valueLength; + maxLength = tdsReader.readUnsignedShort(); + typeInfo.setMaxLength(maxLength); + if (maxLength > DataTypes.SHORT_VARTYPE_MAX_BYTES) + tdsReader.throwInvalidTDS(); + typeInfo.setDisplaySize(2 * maxLength); + typeInfo.setPrecision(maxLength); + typeInfo.ssType = SSType.BINARY; + lengthConsumed = 2 + cbPropsActual; + tempvalueLength = lengthTotal - lengthConsumed; + + convertedValue = DDC.convertStreamToObject(new SimpleInputStream(tdsReader, tempvalueLength, streamGetterArgs, this), typeInfo, + jdbcType, streamGetterArgs); + + break; + case GUID: + typeInfo.ssType = SSType.GUID; + typeInfo.setDisplaySize("NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN".length()); + } + break; // Unknown SSType should have already been rejected by TypeInfo.setFromTDS() default: assert false : "Unexpected SSType " + typeInfo.getSSType(); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantTest.java new file mode 100644 index 0000000000..943f240f21 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantTest.java @@ -0,0 +1,104 @@ +/** + * + */ +package com.microsoft.sqlserver.jdbc.datatypes; + +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.IOException; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.logging.FileHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter; + +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 static org.junit.jupiter.api.Assertions.assertEquals; + +import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.SQLServerException; +import com.microsoft.sqlserver.jdbc.SQLServerResultSet; +import com.microsoft.sqlserver.testframework.AbstractTest; + +/** + * @author v-afrafi + * + */ +@RunWith(JUnitPlatform.class) +public class SQLVariantTest extends AbstractTest { + + SQLServerConnection con = null; + Statement stmt = null; + String tableName = "SqlVariant_Test"; //"charTest"; //// + + + private void createTables() throws SQLServerException { + try { + con = (SQLServerConnection) DriverManager.getConnection(connectionString); + stmt = con.createStatement(); + stmt.executeUpdate("CREATE TABLE " + tableName + " (col1 sql_variant)"); + } + catch (SQLException e) { + fail(e.toString()); + } + finally{ + if (null != con) + con.close(); + } + + } + + /** + * Read from a sql_variant table + * @throws SQLException + * @throws IOException + * @throws SecurityException + */ + @Test + public void readFrom() throws SQLException, SecurityException, IOException { + Handler fh = new FileHandler("C:\\Users\\v-afrafi\\Documents\\mssql-jdbc\\Driver.log"); + fh.setFormatter(new SimpleFormatter()); + fh.setLevel(Level.FINEST); + Logger.getLogger("").addHandler(fh); + // By default, Loggers also send their output to their parent logger.   + // Typically the root Logger is configured with a set of Handlers that essentially act as default handlers for all loggers.  + Logger logger = Logger.getLogger("com.microsoft.sqlserver.jdbc"); + logger.setLevel(Level.FINEST); + + con = (SQLServerConnection) DriverManager.getConnection(connectionString); + stmt = con.createStatement(); +// stmt.executeUpdate("INSERT into " + tableName + " values (2)"); +// PreparedStatement pstmt = con.prepareStatement("INSERT into " + tableName + " values (?)"); +// pstmt.setInt(1, 1); +// pstmt.executeUpdate(); + +// tableName = "dateTest"; + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM "+ tableName); + while (rs.next()){ + System.out.println(rs.getObject(1)); +// assertEquals(rs.getObject(1), 1000.12); + } + + + } + + /** + * drop the tables + * @throws SQLException + */ +// @AfterAll +// public void dropAll() throws SQLException{ +// stmt.executeUpdate("IF EXISTS (select * from sysobjects where id = object_id(N'" + tableName + "') and OBJECTPROPERTY(id, N'IsTable') = 1)" +// + " DROP TABLE " + tableName); +// } + +} From 02dd8c066e0edb4c2abb7c5ba7f404577402a5c6 Mon Sep 17 00:00:00 2001 From: v-afrafi Date: Mon, 27 Mar 2017 10:00:18 -0700 Subject: [PATCH 08/13] removed the unfinished coding parts for stored procedure. This commit will be only for getters and adding the test --- .../microsoft/sqlserver/jdbc/DataTypes.java | 15 +- .../microsoft/sqlserver/jdbc/IOBuffer.java | 14 +- .../sqlserver/jdbc/PLPInputStream.java | 29 +- .../microsoft/sqlserver/jdbc/Parameter.java | 16 - .../sqlserver/jdbc/SQLServerBulkCopy.java | 3 - .../com/microsoft/sqlserver/jdbc/dtv.java | 366 ++++++------- .../jdbc/datatypes/SQLVariantTest.java | 512 +++++++++++++++--- 7 files changed, 648 insertions(+), 307 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java index f9d6c1117f..3a0a142e52 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java @@ -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.CHAR), //TODO: was variant + SQL_VARIANT (Category.VARIANT, "sql_variant", JDBCType.CHAR), UDT (Category.UDT, "udt", JDBCType.VARBINARY), XML (Category.XML, "xml", JDBCType.LONGNVARCHAR), TIMESTAMP (Category.TIMESTAMP, "timestamp", JDBCType.BINARY); @@ -362,14 +362,7 @@ enum GetterConversion VARIANT ( SSType.Category.VARIANT, EnumSet.of( - JDBCType.Category.NUMERIC, - JDBCType.Category.CHARACTER, - JDBCType.Category.BINARY, - JDBCType.Category.CHARACTER, - JDBCType.Category.NCHARACTER, - JDBCType.Category.LONG_CHARACTER, - JDBCType.Category.LONG_NCHARACTER, - JDBCType.Category.Variant)); + JDBCType.Category.CHARACTER)); private final SSType.Category from; private final EnumSet to; @@ -857,7 +850,7 @@ enum JDBCType 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"), - Variant (Category.Variant, microsoft.sql.Types.VARIANT, "java.lang.Object"); + Variant (Category.Variant, microsoft.sql.Types.VARIANT, "java.lang.String"); final Category category; private final int intValue; @@ -997,7 +990,7 @@ enum SetterConversion { JDBCType.Category.CHARACTER, JDBCType.Category.LONG_CHARACTER, JDBCType.Category.NCHARACTER, - JDBCType.Category.LONG_NCHARACTER)), + JDBCType.Category.LONG_NCHARACTER)), DATE ( JDBCType.Category.DATE, diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 0cb6a02fc5..824417c98c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -6423,19 +6423,7 @@ final void readBytes(byte[] value, bytesRead += bytesToCopy; payloadOffset += bytesToCopy; } - } - - final int readSqlVariant( ) throws SQLServerException{ - - if (payloadOffset + 4 <= currentPacket.payloadLength) { - payloadOffset += 4; - int value = readUnsignedByte(); - return value; - } - return 0; - - - } + } final byte[] readWrappedBytes(int valueLength) throws SQLServerException { assert valueLength <= valueBytes.length; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/PLPInputStream.java b/src/main/java/com/microsoft/sqlserver/jdbc/PLPInputStream.java index 1b8b41facd..d0830ca9a7 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/PLPInputStream.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/PLPInputStream.java @@ -43,23 +43,22 @@ class PLPInputStream extends BaseInputStream { final static boolean isNull(TDSReader tdsReader) throws SQLServerException { TDSReaderMark mark = tdsReader.mark(); try { -// -// } -// PLPInputStream tempPLP = PLPInputStream.makeTempStream(tdsReader, false, null); - return null == PLPInputStream.makeTempStream(tdsReader, false, null); -// try { -// if (null != tempPLP) { -// tempPLP.close(); -// return false; -// } -// } -// catch (IOException e) { -// tdsReader.getConnection().terminate(SQLServerException.DRIVER_ERROR_IO_FAILED, e.getMessage()); -// } -// return true; + PLPInputStream tempPLP = PLPInputStream.makeTempStream(tdsReader, false, null); + try { + if (null != tempPLP) { + tempPLP.close(); + return false; + } + } + catch (IOException e) { + tdsReader.getConnection().terminate(SQLServerException.DRIVER_ERROR_IO_FAILED, e.getMessage()); + } + return true; } finally { -// if (null != tdsReader) + + if (null != tdsReader) + tdsReader.reset(mark); } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java index 4482f094dc..841582cd33 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java @@ -884,11 +884,6 @@ else if ((null != jdbcTypeSetByUser) && ((jdbcTypeSetByUser == JDBCType.NVARCHAR case GUID: param.typeDefinition = SSType.GUID.toString(); - break; - case Variant: - // param.typeDefinition = SSType.INTEGER.toString(); - param.typeDefinition = SSType.SQL_VARIANT.toString(); - break; default: assert false : "Unexpected JDBC type " + dtv.getJdbcType(); @@ -1143,17 +1138,6 @@ void execute(DTV dtv, com.microsoft.sqlserver.jdbc.TVP tvpValue) throws SQLServerException { 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 { - // TODO Auto-generated method stub - - } - } /** diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java index d771333464..e35e98a489 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java @@ -1438,9 +1438,6 @@ private String getDestTypeFromSrcType(int srcColIndx, else { return "datetimeoffset(" + bulkScale + ")"; } - case microsoft.sql.Types.VARIANT: - return "sql_variant"; - default: { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_BulkTypeNotSupported")); Object[] msgArgs = {JDBCType.of(bulkJdbcType).toString().toLowerCase(Locale.ENGLISH)}; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java index 50faf5e5cc..c0667ee537 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java @@ -142,8 +142,6 @@ abstract void execute(DTV dtv, abstract void execute(DTV dtv, TVP tvpValue) throws SQLServerException; - abstract void execute(DTV dtv, - SqlVariant SqlVariantValue) throws SQLServerException; } /** @@ -1442,15 +1440,8 @@ void execute(DTV dtv, 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 { - // TODO Auto-generated method stub - - } + + } /** @@ -1595,9 +1586,6 @@ final void executeOp(DTVExecuteOp op) throws SQLServerException { case STRUCT: unsupportedConversion = true; break; - case Variant: - op.execute(this, (microsoft.sql.SqlVariant) null); - break; case UNKNOWN: default: assert false : "Unexpected JDBCType: " + jdbcType; @@ -1619,6 +1607,9 @@ final void executeOp(DTVExecuteOp op) throws SQLServerException { byte[] bArray = Util.asGuidByteArray((UUID) value); op.execute(this, bArray); } + else if (jdbcType.Variant == jdbcType){ + op.execute(this, String.valueOf(value)); + } else { if (null != cryptoMeta) { // if streaming types check for allowed data length in AE @@ -2303,15 +2294,7 @@ else if (null != 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 { - // TODO Auto-generated method stub - - } + } void setValue(DTV dtv, @@ -2418,7 +2401,7 @@ final class TypeInfo { private int displaySize;// size is in characters. display size assumes a formatted hexa decimal representation for binaries. private int scale; private short flags; - public SSType ssType; + private SSType ssType; private int userType; private String udtTypeName; @@ -2429,6 +2412,10 @@ final class TypeInfo { SSType getSSType() { return ssType; } + + void setSSType(SSType ssType) { + this.ssType = ssType; + } SSLenType getSSLenType() { return ssLenType; @@ -3067,57 +3054,7 @@ public void apply(TypeInfo typeInfo, typeInfo.ssLenType = SSLenType.LONGLENTYPE; //Variant type should be LONGLENTYPE length. typeInfo.maxLength = tdsReader.readInt(); typeInfo.ssType = SSType.SQL_VARIANT; -// typeInfo.precision = 255; -// typeInfo.scale = 255; -// switch (TDSType.valueOf(tdsReader.readSqlVariant())) -// { -// case INTN: -// break; -// case INT4: -// break; -// } - -// int type = tdsReader.readSqlVariant();//tdsReader.readUnsignedByte(); -// int cbPropsActual = tdsReader.readUnsignedByte(); -//// System.out.println(TDSType.valueOf(type)); -// switch(TDSType.valueOf(type)){ -// case INT4: -// INTEGER.build(typeInfo, tdsReader); -// break; -// } - } -// 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(); -// } -// } }); private final TDSType tdsType; @@ -3571,12 +3508,12 @@ private void getValuePrep(TypeInfo typeInfo, } } - else { + else if(SSType.SQL_VARIANT == typeInfo.getSSType()) { valueLength = tdsReader.readInt(); isNull = (0 == valueLength); } if (SSType.SQL_VARIANT == typeInfo.getSSType()){ - typeInfo.ssType = SSType.SQL_VARIANT; + typeInfo.setSSType(SSType.SQL_VARIANT); } break; } @@ -4017,9 +3954,16 @@ Object getValue(DTV dtv, convertedValue = tdsReader.readGUID(valueLength, jdbcType, streamGetterArgs.streamType); break; - case SQL_VARIANT: + 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(); + int cbPropsActual = tdsReader.readUnsignedByte(); switch(TDSType.valueOf(baseType)){ case INT8: convertedValue = DDC.convertLongToObject(tdsReader.readLong(), jdbcType, baseSSType, streamGetterArgs.streamType); @@ -4034,114 +3978,9 @@ Object getValue(DTV dtv, convertedValue = DDC.convertIntegerToObject(tdsReader.readUnsignedByte(), valueLength, jdbcType, streamGetterArgs.streamType); break; - case DECIMALN: - case NUMERICN: - int precision = tdsReader.readUnsignedByte(); - typeInfo.setScale( tdsReader.readUnsignedByte() ); - int lengthTotal = valueLength; - int lengthConsumed = 2 + cbPropsActual; - int tempvalueLength = lengthTotal - lengthConsumed; - convertedValue = tdsReader.readDecimal(tempvalueLength, typeInfo, jdbcType, streamGetterArgs.streamType); - break; - - case MONEY4: - lengthTotal = valueLength; - lengthConsumed = 2 + cbPropsActual; - tempvalueLength = lengthTotal - lengthConsumed; - typeInfo.ssType = SSType.SMALLMONEY; - typeInfo.setMaxLength(4); - typeInfo.setPrecision(Long.toString(Long.MAX_VALUE).length()); - typeInfo.setDisplaySize( ("-" + "." + Integer.toString(Integer.MAX_VALUE)).length()); - typeInfo.setScale(4); - - convertedValue = tdsReader.readMoney(tempvalueLength, jdbcType, streamGetterArgs.streamType); - break; - case MONEY8: - lengthTotal = valueLength; - lengthConsumed = 2 + cbPropsActual; - tempvalueLength = lengthTotal - lengthConsumed; - typeInfo.ssType = SSType.MONEY; - typeInfo.setMaxLength(8); - typeInfo.setPrecision(Long.toString(Long.MAX_VALUE).length()); - typeInfo.setDisplaySize( ("-" + "." + Integer.toString(Integer.MAX_VALUE)).length()); - typeInfo.setScale(4); - - convertedValue = tdsReader.readMoney(tempvalueLength, jdbcType, streamGetterArgs.streamType); - break; - case BIGVARCHAR: - lengthTotal = valueLength; - lengthConsumed = 2 + cbPropsActual; - tempvalueLength = lengthTotal - lengthConsumed; - - SQLCollation collation = null; - collation = tdsReader.readCollation(); - typeInfo.setSQLCollation(collation); - - - typeInfo.setSSLenType(SSLenType.USHORTLENTYPE); - int maxLength = tdsReader.readUnsignedShort(); - typeInfo.setMaxLength(maxLength); - if (maxLength > DataTypes.SHORT_VARTYPE_MAX_BYTES) - tdsReader.throwInvalidTDS(); - typeInfo.setDisplaySize(maxLength); - typeInfo.setPrecision(maxLength); - -// typeInfo.ssType = SSType.CHAR; - typeInfo.setCharset(collation.getCharset()); - - convertedValue = DDC.convertStreamToObject(new SimpleInputStream(tdsReader, tempvalueLength, streamGetterArgs, this), typeInfo, - jdbcType, streamGetterArgs); - break; - - case DATETIME8: - case DATETIME4: -// typeInfo.setScale( 3); - lengthTotal = valueLength; - lengthConsumed = 2 + cbPropsActual; - tempvalueLength = lengthTotal - lengthConsumed; - convertedValue = tdsReader.readDateTime(tempvalueLength, cal, jdbcType, streamGetterArgs.streamType); - break; - case DATEN: - lengthTotal = valueLength; - lengthConsumed = 2 + cbPropsActual; - tempvalueLength = lengthTotal - lengthConsumed; - convertedValue = tdsReader.readDate(tempvalueLength, cal, jdbcType); - break; - case TIMEN: - lengthTotal = valueLength; - typeInfo.setScale(tdsReader.readUnsignedByte()); - lengthConsumed = 2 + cbPropsActual; - tempvalueLength = lengthTotal - lengthConsumed; - convertedValue = tdsReader.readTime(tempvalueLength, typeInfo, cal, jdbcType); - break; - case DATETIME2N: - lengthTotal = valueLength; - typeInfo.setScale(tdsReader.readUnsignedByte()); - lengthConsumed = 2 + cbPropsActual; - tempvalueLength = lengthTotal - lengthConsumed; - convertedValue = tdsReader.readDateTime2(tempvalueLength, typeInfo, cal, jdbcType); - break; - case BIGBINARY: - case BIGVARBINARY: - lengthTotal = valueLength; - maxLength = tdsReader.readUnsignedShort(); - typeInfo.setMaxLength(maxLength); - if (maxLength > DataTypes.SHORT_VARTYPE_MAX_BYTES) - tdsReader.throwInvalidTDS(); - typeInfo.setDisplaySize(2 * maxLength); - typeInfo.setPrecision(maxLength); - typeInfo.ssType = SSType.BINARY; - lengthConsumed = 2 + cbPropsActual; - tempvalueLength = lengthTotal - lengthConsumed; - - convertedValue = DDC.convertStreamToObject(new SimpleInputStream(tdsReader, tempvalueLength, streamGetterArgs, this), typeInfo, - jdbcType, streamGetterArgs); - - break; - case GUID: - typeInfo.ssType = SSType.GUID; - typeInfo.setDisplaySize("NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN".length()); - + default: + convertedValue = readSqlVariant(TDSType.valueOf(baseType), cbPropsActual, valueLength, tdsReader, baseSSType, typeInfo, jdbcType, streamGetterArgs, cal); + break; } break; // Unknown SSType should have already been rejected by TypeInfo.setFromTDS() @@ -4155,6 +3994,161 @@ Object getValue(DTV dtv, assert isNull || null != convertedValue; return convertedValue; } + + /** + * Read the value inside sqlVariant + * @throws SQLServerException + */ + private Object readSqlVariant(TDSType baseType, int cbPropsActual, int valueLength, TDSReader tdsReader, SSType baseSSType, TypeInfo typeInfo, JDBCType jdbcType, + InputStreamGetterArgs streamGetterArgs, Calendar cal) throws SQLServerException{ + Object convertedValue = null; + int lengthTotal = valueLength; + int lengthConsumed = 2 + cbPropsActual; + int expectedValueLength = lengthTotal - lengthConsumed; + SQLCollation collation = null; + switch(baseType){ + case DECIMALN: + case NUMERICN: + int precision = tdsReader.readUnsignedByte(); + typeInfo.setScale( tdsReader.readUnsignedByte() ); + convertedValue = tdsReader.readDecimal(expectedValueLength, typeInfo, jdbcType, streamGetterArgs.streamType); + break; + case FLOAT4: + convertedValue = tdsReader.readReal(expectedValueLength, jdbcType, streamGetterArgs.streamType); + break; + case FLOAT8: + convertedValue = tdsReader.readFloat(expectedValueLength, jdbcType, streamGetterArgs.streamType); + break; + case MONEY4: + typeInfo.setMaxLength(4); + typeInfo.setPrecision(Long.toString(Long.MAX_VALUE).length()); + typeInfo.setDisplaySize( ("-" + "." + Integer.toString(Integer.MAX_VALUE)).length()); + typeInfo.setScale(4); + + convertedValue = tdsReader.readMoney(expectedValueLength, jdbcType, streamGetterArgs.streamType); + break; + case MONEY8: + typeInfo.setMaxLength(8); + typeInfo.setPrecision(Long.toString(Long.MAX_VALUE).length()); + typeInfo.setDisplaySize( ("-" + "." + Integer.toString(Integer.MAX_VALUE)).length()); + typeInfo.setScale(4); + + convertedValue = tdsReader.readMoney(expectedValueLength, jdbcType, streamGetterArgs.streamType); + break; + case BIT1: + case BITN: + 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: + collation = tdsReader.readCollation(); + typeInfo.setSQLCollation(collation); + typeInfo.setSSLenType(SSLenType.USHORTLENTYPE); + int maxLength = tdsReader.readUnsignedShort(); + typeInfo.setMaxLength(maxLength); + if (maxLength > DataTypes.SHORT_VARTYPE_MAX_BYTES) + tdsReader.throwInvalidTDS(); + typeInfo.setDisplaySize(maxLength); + typeInfo.setPrecision(maxLength); + typeInfo.setCharset(collation.getCharset()); + convertedValue = DDC.convertStreamToObject(new SimpleInputStream(tdsReader, expectedValueLength, streamGetterArgs, this), typeInfo, + jdbcType, streamGetterArgs); + break; + + case NCHAR: + 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); + typeInfo.setCharset(Encoding.UNICODE.charset()); + convertedValue = DDC.convertStreamToObject(new SimpleInputStream(tdsReader, expectedValueLength, streamGetterArgs, this), typeInfo, + jdbcType, streamGetterArgs); + break; + case NVARCHAR: + collation = tdsReader.readCollation(); + typeInfo.setSQLCollation(collation); + + maxLength = tdsReader.readUnsignedShort(); + // for PLP types + if (DataTypes.MAXTYPE_LENGTH == maxLength) { + typeInfo.setDisplaySize(DataTypes.MAX_VARTYPE_MAX_CHARS); + typeInfo.setPrecision(DataTypes.MAX_VARTYPE_MAX_CHARS); + } + // for non-PLP types + else if (maxLength <= DataTypes.SHORT_VARTYPE_MAX_BYTES && 0 == maxLength % 2) { + typeInfo.setDisplaySize(maxLength / 2); + typeInfo.setPrecision(maxLength / 2); + } + else { + tdsReader.throwInvalidTDS(); + } + typeInfo.setCharset(Encoding.UNICODE.charset()); + convertedValue = DDC.convertStreamToObject(new SimpleInputStream(tdsReader, expectedValueLength, streamGetterArgs, this), typeInfo, + jdbcType, streamGetterArgs); + break; + case DATETIME8: + case DATETIME4: + convertedValue = tdsReader.readDateTime(expectedValueLength, cal, jdbcType, streamGetterArgs.streamType); + break; + case DATEN: + convertedValue = tdsReader.readDate(expectedValueLength, cal, jdbcType); + break; + case TIMEN: + typeInfo.setScale(tdsReader.readUnsignedByte()); + convertedValue = tdsReader.readTime(expectedValueLength, typeInfo, cal, jdbcType); + break; + case DATETIME2N: + typeInfo.setScale(tdsReader.readUnsignedByte()); + convertedValue = tdsReader.readDateTime2(expectedValueLength, typeInfo, cal, jdbcType); + break; + case BIGBINARY: + case BIGVARBINARY: + maxLength = tdsReader.readUnsignedShort(); + typeInfo.setMaxLength(maxLength); + if (maxLength > DataTypes.SHORT_VARTYPE_MAX_BYTES) + tdsReader.throwInvalidTDS(); + typeInfo.setDisplaySize(2 * maxLength); + typeInfo.setPrecision(maxLength); + jdbcType = JDBCType.BINARY; + convertedValue = DDC.convertStreamToObject(new SimpleInputStream(tdsReader, expectedValueLength, streamGetterArgs, this), typeInfo, + jdbcType, streamGetterArgs); + break; + case GUID: + 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 SSType " + typeInfo.getSSType(); + break; + } + return convertedValue; + } Object getSetterValue() { // This function is never called, but must be implemented; it's abstract in DTVImpl. diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantTest.java index 943f240f21..2f1d8ecc77 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantTest.java @@ -1,104 +1,490 @@ -/** +/* + * 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.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.util.logging.FileHandler; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.logging.SimpleFormatter; +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 static org.junit.jupiter.api.Assertions.assertEquals; 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.nimbusds.oauth2.sdk.ParseException; /** - * @author v-afrafi + * Tests for supporting sqlVariant * */ @RunWith(JUnitPlatform.class) public class SQLVariantTest extends AbstractTest { - - SQLServerConnection con = null; - Statement stmt = null; - String tableName = "SqlVariant_Test"; //"charTest"; //// - - - private void createTables() throws SQLServerException { - try { - con = (SQLServerConnection) DriverManager.getConnection(connectionString); - stmt = con.createStatement(); - stmt.executeUpdate("CREATE TABLE " + tableName + " (col1 sql_variant)"); + + static SQLServerConnection con = null; + static Statement stmt = null; + static String tableName = "SqlVariant_Test"; + + /** + * Read from a SqlVariant table int value + * + * @throws SQLException + * @throws IOException + * @throws SecurityException + */ + @Test + public void readInt() throws SQLException, SecurityException, IOException { + int value = 2; + createAndPopulateTable("int", value); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertEquals(rs.getObject(1), String.valueOf(value)); + } + + } + + /** + * Read money type stored in SqlVariant + * + * @throws SQLException + * + */ + @Test + public void readMoney() throws SQLException { + Double value = 123.12; + createAndPopulateTable("Money", value); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertEquals(rs.getObject(1), "123.1200"); + } + } + + /** + * Reading smallmoney from SqlVariant + * + * @throws SQLException + */ + @Test + public void readSmallMoney() throws SQLException { + Double value = 123.12; + createAndPopulateTable("smallmoney", value); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertEquals(rs.getObject(1), "123.1200"); + } + } + + /** + * Read GUID stored in SqlVariant + * + * @throws SQLException + */ + @Test + public void readGUID() throws SQLException { + String value = "1AE740A2-2272-4B0F-8086-3DDAC595BC11";// NEWID()"; + createAndPopulateTable("uniqueidentifier", "'" + value + "'"); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertEquals(rs.getObject(1), value); } - catch (SQLException e) { - fail(e.toString()); + } + + /** + * Reading date stored in SqlVariant + * + * @throws SQLException + */ + @Test + public void readDate() throws SQLException { + String value = "'2015-05-08'"; + createAndPopulateTable("date", value); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertEquals(rs.getObject(1), "2015-05-08"); } - finally{ - if (null != con) - con.close(); + } + + /** + * Read time from SqlVariant + * + * @throws SQLException + */ + @Test + public void readTime() throws SQLException { + String value = "'12:26:27.123'"; + createAndPopulateTable("time(3)", value); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertEquals(rs.getObject(1), "12:26:27.123"); } - + } + + /** + * Read datetime from SqlVariant + * + * @throws SQLException + */ + @Test + public void readDateTime() throws SQLException { + String value = "'2015-05-08 12:26:24'"; + createAndPopulateTable("datetime", value); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (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); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (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); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (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); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertEquals(rs.getObject(1), "5.0"); + } + } + + /** + * Read bigint from SqlVariant + * + * @throws SQLException + */ + @Test + public void readBigInt() throws SQLException { + int value = 5; + createAndPopulateTable("bigint", value); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertEquals(rs.getObject(1), "5"); + } + } + + /** + * read smallint from SqlVariant + * + * @throws SQLException + */ + @Test + public void readSmallInt() throws SQLException { + int value = 5; + createAndPopulateTable("smallint", value); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertEquals(rs.getObject(1), "5"); + } + } + + /** + * Read tinyint from SqlVariant + * + * @throws SQLException + */ + @Test + public void readTinyInt() throws SQLException { + int value = 5; + createAndPopulateTable("tinyint", value); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertEquals(rs.getString(1), "5"); + } + } + + /** + * read bit from SqlVariant + * + * @throws SQLException + */ + @Test + public void readBit() throws SQLException { + int value = 50000; + createAndPopulateTable("bit", value); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertEquals(rs.getObject(1), "1"); + } + } + + /** + * Read float from SqlVariant + * + * @throws SQLException + */ + @Test + public void readReal() throws SQLException { + float value = 5; + createAndPopulateTable("Real", value); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertEquals(rs.getObject(1), "5.0"); + } + } + + /** + * Read nchar from SqlVariant + * + * @throws SQLException + */ + @Test + public void readNChar() throws SQLException, SecurityException, IOException { + String value = "nchar"; + createAndPopulateTable("nchar(5)", "'" + value + "'"); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertEquals(rs.getObject(1), value); + } + } + + @Test + public void readNVarChar() throws SQLException, SecurityException, IOException { + String value = "nvarchar"; + createAndPopulateTable("nvarchar(10)", "'" + value + "'"); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertEquals(rs.getObject(1), value); + } + } + + @Test + public void readBinary20() throws SQLException, SecurityException, IOException { + String value = "hi"; + createAndPopulateTable("binary(20)", "'" + value + "'"); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertTrue(parseByte((byte[]) rs.getObject(1), (byte[]) value.getBytes())); + } + } + + @Test + public void readVarBinary20() throws SQLException, SecurityException, IOException { + String value = "hi"; + createAndPopulateTable("varbinary(20)", "'" + value + "'"); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertTrue(parseByte((byte[]) rs.getObject(1), (byte[]) value.getBytes())); + } + } + + @Test + public void readBinary512() throws SQLException, SecurityException, IOException { + String value = "hi"; + createAndPopulateTable("binary(512)", "'" + value + "'"); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertTrue(parseByte((byte[]) rs.getObject(1), (byte[]) value.getBytes())); + } + } + + @Test + public void readVarBinary8000() throws SQLException, SecurityException, IOException { + String value = "hi"; + createAndPopulateTable("binary(8000)", "'" + value + "'"); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertTrue(parseByte((byte[]) rs.getObject(1), (byte[]) value.getBytes())); + } + } + + 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; + } + + /** + * 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"); + } + stmt.executeUpdate("IF EXISTS (select * from sysobjects where id = object_id(N'" + tableName + "') " + + "and OBJECTPROPERTY(id, N'IsTable') = 1)" + " DROP TABLE " + tableName); + 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); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertEquals(rs.getObject(1), buffer.toString()); + } + } + + /** + * Testing inserting and reading from SqlVariant and int column + * + * @throws SQLException + */ + @Test + public void insert() throws SQLException { + stmt.executeUpdate("IF EXISTS (select * from sysobjects where id = object_id(N'" + tableName + "') " + + "and OBJECTPROPERTY(id, N'IsTable') = 1)" + " DROP TABLE " + tableName); + 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(); + + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + int i = 0; + while (rs.next()) { + assertEquals(rs.getObject(1), col1Value[i]); + assertEquals(rs.getObject(2), col2Value[i]); + i++; + } + } + + /** + * Test inserting using setObject + * + * @throws SQLException + * @throws ParseException + */ + @Test + public void test() throws SQLException { + stmt.executeUpdate("IF EXISTS (select * from sysobjects where id = object_id(N'" + tableName + "') " + + "and OBJECTPROPERTY(id, N'IsTable') = 1)" + " DROP TABLE " + tableName); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant)"); + SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con.prepareStatement("insert into " + tableName + " values (?)"); + + pstmt.setObject(1, 2); + pstmt.execute(); + + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName); + while (rs.next()) { + assertEquals(rs.getObject(1), String.valueOf(2)); + } + } + + /** + * Create and populate table + * + * @param columnType + * @param value + * @throws SQLException + */ + public void createAndPopulateTable(String columnType, + Object value) throws SQLException { + stmt.executeUpdate("IF EXISTS (select * from sysobjects where id = object_id(N'" + tableName + "') " + + "and OBJECTPROPERTY(id, N'IsTable') = 1)" + " DROP TABLE " + tableName); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant)"); + stmt.executeUpdate("INSERT into " + tableName + " values (CAST (" + value + " AS " + columnType + "))"); } + /** - * Read from a sql_variant table - * @throws SQLException - * @throws IOException - * @throws SecurityException - */ - @Test - public void readFrom() throws SQLException, SecurityException, IOException { - Handler fh = new FileHandler("C:\\Users\\v-afrafi\\Documents\\mssql-jdbc\\Driver.log"); - fh.setFormatter(new SimpleFormatter()); - fh.setLevel(Level.FINEST); - Logger.getLogger("").addHandler(fh); - // By default, Loggers also send their output to their parent logger.   - // Typically the root Logger is configured with a set of Handlers that essentially act as default handlers for all loggers.  - Logger logger = Logger.getLogger("com.microsoft.sqlserver.jdbc"); - logger.setLevel(Level.FINEST); - + * 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(); -// stmt.executeUpdate("INSERT into " + tableName + " values (2)"); -// PreparedStatement pstmt = con.prepareStatement("INSERT into " + tableName + " values (?)"); -// pstmt.setInt(1, 1); -// pstmt.executeUpdate(); - -// tableName = "dateTest"; - SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM "+ tableName); - while (rs.next()){ - System.out.println(rs.getObject(1)); -// assertEquals(rs.getObject(1), 1000.12); - } - - } - + /** * drop the tables + * * @throws SQLException */ -// @AfterAll -// public void dropAll() throws SQLException{ -// stmt.executeUpdate("IF EXISTS (select * from sysobjects where id = object_id(N'" + tableName + "') and OBJECTPROPERTY(id, N'IsTable') = 1)" -// + " DROP TABLE " + tableName); -// } + @AfterAll + public static void afterAll() throws SQLException { + stmt.executeUpdate("IF EXISTS (select * from sysobjects where id = object_id(N'" + tableName + "') and OBJECTPROPERTY(id, N'IsTable') = 1)" + + " DROP TABLE " + tableName); + if (null != stmt) { + stmt.close(); + } + if (null != con) { + con.close(); + } + } } From c3088f395347033195e3cf5adf0ac5b473b14a5d Mon Sep 17 00:00:00 2001 From: v-afrafi Date: Fri, 31 Mar 2017 13:55:37 -0700 Subject: [PATCH 09/13] sql_variant storedProcedure support --- .../microsoft/sqlserver/jdbc/DataTypes.java | 24 +-- .../microsoft/sqlserver/jdbc/IOBuffer.java | 20 +++ .../microsoft/sqlserver/jdbc/Parameter.java | 13 ++ .../com/microsoft/sqlserver/jdbc/dtv.java | 38 ++++- .../jdbc/datatypes/SQLVariantTest.java | 140 ++++++++++++++++-- 5 files changed, 204 insertions(+), 31 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java index 3a0a142e52..6f1493497e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java @@ -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.CHAR), + SQL_VARIANT (Category.Sql_Variant, "sql_variant", JDBCType.CHAR), 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 } @@ -359,10 +359,11 @@ enum GetterConversion EnumSet.of( JDBCType.Category.BINARY, JDBCType.Category.CHARACTER)), - VARIANT ( - SSType.Category.VARIANT, + Sql_Variant ( + SSType.Category.Sql_Variant, EnumSet.of( - JDBCType.Category.CHARACTER)); + JDBCType.Category.CHARACTER, + JDBCType.Category.Sql_Variant)); private final SSType.Category from; private final EnumSet to; @@ -850,7 +851,9 @@ enum JDBCType 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"), - Variant (Category.Variant, microsoft.sql.Types.VARIANT, "java.lang.String"); +// Variant (Category.Variant, microsoft.sql.Types.VARIANT, "java.lang.String"); + Sql_Variant (Category.Sql_Variant, microsoft.sql.Types.VARIANT, "java.lang.String"); + final Category category; private final int intValue; @@ -898,7 +901,8 @@ enum Category { UNKNOWN, TVP, GUID, - Variant; + // Variant; + Sql_Variant; } // This SetterConversion enum is based on the Category enum @@ -990,7 +994,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, @@ -1193,7 +1198,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, diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 824417c98c..6619e16f24 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -69,6 +69,8 @@ import javax.net.ssl.X509TrustManager; import javax.xml.bind.DatatypeConverter; +import microsoft.sql.SqlVariant; + final class TDS { // TDS protocol versions static final int VER_DENALI = 0x74000004; // TDS 7.4 @@ -4253,6 +4255,18 @@ void writeRPCReal(String sName, writeInt(Float.floatToRawIntBits(floatValue.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 + } + } /** * Append a double value in RPC transmission format. @@ -5400,6 +5414,12 @@ void writeRPCDateTimeOffset(String sName, writeShort((short) minutesOffset); } + + void writeRPCSQLVariant(String sName, + String value, + boolean bOut) throws SQLServerException { + writeRPCStringUnicode(value); + } /** * Returns subSecondNanos rounded to the maximum precision supported. The maximum fractional scale is MAX_FRACTIONAL_SECONDS_SCALE(7). Eg1: if you diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java index 841582cd33..45685fb449 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java @@ -885,6 +885,9 @@ 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; @@ -1138,6 +1141,16 @@ void execute(DTV dtv, com.microsoft.sqlserver.jdbc.TVP tvpValue) throws SQLServerException { 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/dtv.java b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java index c0667ee537..1c1463a655 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java @@ -142,6 +142,9 @@ abstract void execute(DTV dtv, abstract void execute(DTV dtv, TVP tvpValue) throws SQLServerException; + + abstract void execute(DTV dtv, + SqlVariant SqlVariantValue) throws SQLServerException; } /** @@ -1440,8 +1443,15 @@ void execute(DTV dtv, 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); + + } } /** @@ -1586,6 +1596,10 @@ final void executeOp(DTVExecuteOp op) throws SQLServerException { case STRUCT: unsupportedConversion = true; break; + case Sql_Variant: + op.execute(this, (microsoft.sql.SqlVariant) null); + break; + case UNKNOWN: default: assert false : "Unexpected JDBCType: " + jdbcType; @@ -1607,7 +1621,7 @@ final void executeOp(DTVExecuteOp op) throws SQLServerException { byte[] bArray = Util.asGuidByteArray((UUID) value); op.execute(this, bArray); } - else if (jdbcType.Variant == jdbcType){ + else if (jdbcType.Sql_Variant == jdbcType){ op.execute(this, String.valueOf(value)); } else { @@ -2294,6 +2308,16 @@ else if (null != 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 { + // TODO Auto-generated method stub + + } + } @@ -4076,14 +4100,14 @@ private Object readSqlVariant(TDSType baseType, int cbPropsActual, int valueLeng break; case NCHAR: - collation = tdsReader.readCollation(); - typeInfo.setSQLCollation(collation); - typeInfo.setSSLenType(SSLenType.USHORTLENTYPE); + 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); + typeInfo.setPrecision(maxLength / 2); typeInfo.setCharset(Encoding.UNICODE.charset()); convertedValue = DDC.convertStreamToObject(new SimpleInputStream(tdsReader, expectedValueLength, streamGetterArgs, this), typeInfo, jdbcType, streamGetterArgs); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantTest.java index 2f1d8ecc77..381f3a5337 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantTest.java @@ -11,6 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; +import java.sql.CallableStatement; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; @@ -39,14 +40,8 @@ public class SQLVariantTest extends AbstractTest { static SQLServerConnection con = null; static Statement stmt = null; static String tableName = "SqlVariant_Test"; + static String inputProc = "sqlVariant_Proc"; - /** - * Read from a SqlVariant table int value - * - * @throws SQLException - * @throws IOException - * @throws SecurityException - */ @Test public void readInt() throws SQLException, SecurityException, IOException { int value = 2; @@ -288,6 +283,12 @@ public void readNChar() throws SQLException, SecurityException, IOException { } } + /** + * Read nVarChar + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ @Test public void readNVarChar() throws SQLException, SecurityException, IOException { String value = "nvarchar"; @@ -298,6 +299,12 @@ public void readNVarChar() throws SQLException, SecurityException, IOException { } } + /** + * readBinary + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ @Test public void readBinary20() throws SQLException, SecurityException, IOException { String value = "hi"; @@ -308,6 +315,12 @@ public void readBinary20() throws SQLException, SecurityException, IOException { } } + /** + * read varBinary + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ @Test public void readVarBinary20() throws SQLException, SecurityException, IOException { String value = "hi"; @@ -318,6 +331,12 @@ public void readVarBinary20() throws SQLException, SecurityException, IOExceptio } } + /** + * read Binary512 + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ @Test public void readBinary512() throws SQLException, SecurityException, IOException { String value = "hi"; @@ -328,6 +347,12 @@ public void readBinary512() throws SQLException, SecurityException, IOException } } + /** + * read Binary(8000) + * @throws SQLException + * @throws SecurityException + * @throws IOException + */ @Test public void readVarBinary8000() throws SQLException, SecurityException, IOException { String value = "hi"; @@ -362,7 +387,7 @@ public void insertVarChar8001() throws SQLException { + "and OBJECTPROPERTY(id, N'IsTable') = 1)" + " DROP TABLE " + tableName); stmt.executeUpdate("create table " + tableName + " (col1 sql_variant)"); SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con.prepareStatement("insert into " + tableName + " values (?)"); - pstmt.setObject(1, buffer.toString()); + pstmt.setSQLVariant(1, buffer.toString()); try { pstmt.execute(); } @@ -396,7 +421,7 @@ public void readNvarChar4000() throws SQLException { * @throws SQLException */ @Test - public void insert() throws SQLException { + public void insertTest() throws SQLException { stmt.executeUpdate("IF EXISTS (select * from sysobjects where id = object_id(N'" + tableName + "') " + "and OBJECTPROPERTY(id, N'IsTable') = 1)" + " DROP TABLE " + tableName); stmt.executeUpdate("create table " + tableName + " (col1 sql_variant, col2 int)"); @@ -404,10 +429,10 @@ public void insert() throws SQLException { String[] col1Value = {"Hello", null}; int[] col2Value = {1, 2}; - pstmt.setObject(1, "Hello"); + pstmt.setSQLVariant(1, "Hello"); pstmt.setInt(2, 1); pstmt.execute(); - pstmt.setObject(1, null); + pstmt.setSQLVariant(1, null); pstmt.setInt(2, 2); pstmt.execute(); @@ -427,7 +452,7 @@ public void insert() throws SQLException { * @throws ParseException */ @Test - public void test() throws SQLException { + public void insertSetObject() throws SQLException { stmt.executeUpdate("IF EXISTS (select * from sysobjects where id = object_id(N'" + tableName + "') " + "and OBJECTPROPERTY(id, N'IsTable') = 1)" + " DROP TABLE " + tableName); stmt.executeUpdate("create table " + tableName + " (col1 sql_variant)"); @@ -442,6 +467,87 @@ public void test() throws SQLException { } } + /** + * Test callableStatement with SqlVariant + * + * @throws SQLException + */ + @Test + public void callableStatementOutputTest() throws SQLException { + int value = 5; + stmt.executeUpdate("IF EXISTS (select * from sysobjects where id = object_id(N'" + tableName + "') " + + "and OBJECTPROPERTY(id, N'IsTable') = 1)" + " DROP TABLE " + tableName); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant)"); + stmt.executeUpdate("INSERT into " + tableName + " values (CAST (" + value + " AS " + "int" + "))"); + + String outPutProc = "sqlVariant_Proc"; + String sql = " IF EXISTS (select * from sysobjects where id = object_id(N'" + outPutProc + "') and OBJECTPROPERTY(id, N'IsProcedure') = 1)" + + " DROP PROCEDURE " + outPutProc; + stmt.execute(sql); + sql = "CREATE PROCEDURE " + outPutProc + " @p0 sql_variant OUTPUT AS SELECT TOP 1 @p0=col1 FROM " + tableName; + stmt.execute(sql); + + CallableStatement cs = con.prepareCall(" {call " + outPutProc + " (?) }"); + cs.registerOutParameter(1, microsoft.sql.Types.VARIANT); + cs.execute(); + assertEquals(cs.getString(1), String.valueOf(value)); + } + + /** + * Test stored procedure with input and output params + * @throws SQLException + */ + @Test + public void callableStatementInOutTest() throws SQLException { + int col1Value = 5; + int col2Value = 2; + stmt.executeUpdate("IF EXISTS (select * from sysobjects where id = object_id(N'" + tableName + "') " + + "and OBJECTPROPERTY(id, N'IsTable') = 1)" + " DROP TABLE " + tableName); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant, col2 int)"); + stmt.executeUpdate("INSERT into " + tableName + "(col1, col2) values (CAST (" + col1Value + " AS " + "int" + "), " + col2Value + ")"); + String sql = " IF EXISTS (select * from sysobjects where id = object_id(N'" + inputProc + "') and OBJECTPROPERTY(id, N'IsProcedure') = 1)" + + " DROP PROCEDURE " + inputProc; + stmt.execute(sql); + 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.VARIANT); + cs.setObject(2, col2Value, microsoft.sql.Types.VARIANT); + cs.execute(); + assertEquals(cs.getObject(1), String.valueOf(col1Value)); + } + + /** + * Test stored procedure with input and output and return value + * @throws SQLException + */ + @Test + public void callableStatementInOutRetTest() throws SQLException { + int col1Value = 5; + int col2Value = 2; + int returnValue = 12; + stmt.executeUpdate("IF EXISTS (select * from sysobjects where id = object_id(N'" + tableName + "') " + + "and OBJECTPROPERTY(id, N'IsTable') = 1)" + " DROP TABLE " + tableName); + stmt.executeUpdate("create table " + tableName + " (col1 sql_variant, col2 int)"); + stmt.executeUpdate("INSERT into " + tableName + "(col1, col2) values (CAST (" + col1Value + " AS " + "int" + "), " + col2Value + ")"); + String sql = " IF EXISTS (select * from sysobjects where id = object_id(N'" + inputProc + "') and OBJECTPROPERTY(id, N'IsProcedure') = 1)" + + " DROP PROCEDURE " + inputProc; + stmt.execute(sql); + 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.VARIANT); + cs.registerOutParameter(2, microsoft.sql.Types.VARIANT); + cs.setObject(3, col2Value, microsoft.sql.Types.VARIANT); + cs.execute(); + assertEquals(cs.getString(1), String.valueOf(returnValue)); + assertEquals(cs.getString(2), String.valueOf(col1Value)); + } + /** * Create and populate table * @@ -449,23 +555,23 @@ public void test() throws SQLException { * @param value * @throws SQLException */ - public void createAndPopulateTable(String columnType, + private void createAndPopulateTable(String columnType, Object value) throws SQLException { stmt.executeUpdate("IF EXISTS (select * from sysobjects where id = object_id(N'" + tableName + "') " + "and OBJECTPROPERTY(id, N'IsTable') = 1)" + " DROP TABLE " + tableName); 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 { + public static void setupHere() throws SQLException, SecurityException, IOException { con = (SQLServerConnection) DriverManager.getConnection(connectionString); stmt = con.createStatement(); } @@ -477,8 +583,12 @@ public static void setupHere() throws SQLException, SecurityException, IOExcepti */ @AfterAll public static void afterAll() throws SQLException { + + stmt.executeUpdate(" IF EXISTS (select * from sysobjects where id = object_id(N'" + inputProc + "') and OBJECTPROPERTY(id, N'IsProcedure') = 1)" + + " DROP PROCEDURE " + inputProc); stmt.executeUpdate("IF EXISTS (select * from sysobjects where id = object_id(N'" + tableName + "') and OBJECTPROPERTY(id, N'IsTable') = 1)" + " DROP TABLE " + tableName); + if (null != stmt) { stmt.close(); } From cc5dc53bc24139168942260607d43f631c4e131c Mon Sep 17 00:00:00 2001 From: v-afrafi Date: Fri, 31 Mar 2017 14:35:24 -0700 Subject: [PATCH 10/13] changed the name of variant to sql_variant --- .../microsoft/sqlserver/jdbc/DataTypes.java | 2 +- .../microsoft/sqlserver/jdbc/IOBuffer.java | 5 +-- .../microsoft/sqlserver/jdbc/Parameter.java | 3 +- .../microsoft/sqlserver/jdbc/SqlVariant.java | 13 +++++++ .../com/microsoft/sqlserver/jdbc/dtv.java | 4 +- src/main/java/microsoft/sql/Types.java | 2 +- .../jdbc/datatypes/SQLVariantTest.java | 37 ++++++++++++++----- 7 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/SqlVariant.java diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java index 6f1493497e..87bd0b4b31 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java @@ -852,7 +852,7 @@ enum JDBCType SMALLDATETIME (Category.TIMESTAMP, microsoft.sql.Types.SMALLDATETIME, "java.sql.Timestamp"), GUID (Category.CHARACTER, microsoft.sql.Types.GUID, "java.lang.String"), // Variant (Category.Variant, microsoft.sql.Types.VARIANT, "java.lang.String"); - Sql_Variant (Category.Sql_Variant, microsoft.sql.Types.VARIANT, "java.lang.String"); + Sql_Variant (Category.Sql_Variant, microsoft.sql.Types.SQL_VARIANT, "java.lang.String"); final Category category; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 6619e16f24..47ad332f09 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -69,7 +69,6 @@ import javax.net.ssl.X509TrustManager; import javax.xml.bind.DatatypeConverter; -import microsoft.sql.SqlVariant; final class TDS { // TDS protocol versions @@ -4263,8 +4262,8 @@ void writeRPCSqlVariant(String sName, // Data and length if (null == sqlVariantValue) { - writeInt(0); //max length - writeInt(0); //actual length + writeInt(0); // max length + writeInt(0); // actual length } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java index 45685fb449..ab67331d87 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java @@ -26,7 +26,6 @@ import java.util.Calendar; import java.util.Locale; -import microsoft.sql.SqlVariant; /** * Parameter represents a JDBC parameter value that is supplied with a prepared or callable statement or an updatable result set. Parameter is JDBC @@ -1148,7 +1147,7 @@ void execute(DTV dtv, @Override void execute(DTV dtv, SqlVariant SqlVariantValue) throws SQLServerException { - setTypeDefinition(dtv); + setTypeDefinition(dtv); } } 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..926cdf4202 --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SqlVariant.java @@ -0,0 +1,13 @@ +/* + * 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 class SqlVariant { + +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java index 1c1463a655..62b3d04c10 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java @@ -44,7 +44,7 @@ import com.microsoft.sqlserver.jdbc.JDBCType.Category; import com.microsoft.sqlserver.jdbc.JavaType.SetterConversionAE; -import microsoft.sql.SqlVariant; + /** * Defines an abstraction for execution of type-specific operations on DTV values. @@ -1597,7 +1597,7 @@ final void executeOp(DTVExecuteOp op) throws SQLServerException { unsupportedConversion = true; break; case Sql_Variant: - op.execute(this, (microsoft.sql.SqlVariant) null); + op.execute(this, (SqlVariant) null); break; case UNKNOWN: diff --git a/src/main/java/microsoft/sql/Types.java b/src/main/java/microsoft/sql/Types.java index f517c0b169..50b309b640 100644 --- a/src/main/java/microsoft/sql/Types.java +++ b/src/main/java/microsoft/sql/Types.java @@ -56,5 +56,5 @@ private Types() { /** * */ - public static final int VARIANT = -156; + public static final int SQL_VARIANT = -156; } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantTest.java index 381f3a5337..63dc23d300 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLVariantTest.java @@ -17,6 +17,8 @@ import java.sql.Statement; import java.util.Arrays; +import javax.swing.plaf.synth.SynthSpinnerUI; + import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -362,6 +364,23 @@ public void readVarBinary8000() throws SQLException, SecurityException, IOExcept 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 + "'"); + SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT SQL_VARIANT_PROPERTY(col1,'BaseType') AS 'Base Type'," + + " SQL_VARIANT_PROPERTY(col1,'Precision') AS 'Precision' from " + tableName); + while (rs.next()) { + assertTrue(rs.getString(1).equalsIgnoreCase("binary"), "unexpected baseType, expected: binary, retrieved:" +rs.getString(1) ); + } + } private boolean parseByte(byte[] expectedData, byte[] retrieved) { @@ -387,7 +406,7 @@ public void insertVarChar8001() throws SQLException { + "and OBJECTPROPERTY(id, N'IsTable') = 1)" + " DROP TABLE " + tableName); stmt.executeUpdate("create table " + tableName + " (col1 sql_variant)"); SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con.prepareStatement("insert into " + tableName + " values (?)"); - pstmt.setSQLVariant(1, buffer.toString()); + pstmt.setObject(1, buffer.toString()); try { pstmt.execute(); } @@ -429,10 +448,10 @@ public void insertTest() throws SQLException { String[] col1Value = {"Hello", null}; int[] col2Value = {1, 2}; - pstmt.setSQLVariant(1, "Hello"); + pstmt.setObject(1, "Hello"); pstmt.setInt(2, 1); pstmt.execute(); - pstmt.setSQLVariant(1, null); + pstmt.setObject(1, null); pstmt.setInt(2, 2); pstmt.execute(); @@ -488,7 +507,7 @@ public void callableStatementOutputTest() throws SQLException { stmt.execute(sql); CallableStatement cs = con.prepareCall(" {call " + outPutProc + " (?) }"); - cs.registerOutParameter(1, microsoft.sql.Types.VARIANT); + cs.registerOutParameter(1, microsoft.sql.Types.SQL_VARIANT); cs.execute(); assertEquals(cs.getString(1), String.valueOf(value)); } @@ -513,8 +532,8 @@ public void callableStatementInOutTest() throws SQLException { stmt.execute(sql); CallableStatement cs = con.prepareCall(" {call " + inputProc + " (?,?) }"); - cs.registerOutParameter(1, microsoft.sql.Types.VARIANT); - cs.setObject(2, col2Value, microsoft.sql.Types.VARIANT); + cs.registerOutParameter(1, microsoft.sql.Types.SQL_VARIANT); + cs.setObject(2, col2Value, microsoft.sql.Types.SQL_VARIANT); cs.execute(); assertEquals(cs.getObject(1), String.valueOf(col1Value)); } @@ -540,9 +559,9 @@ public void callableStatementInOutRetTest() throws SQLException { stmt.execute(sql); CallableStatement cs = con.prepareCall(" {? = call " + inputProc + " (?,?) }"); - cs.registerOutParameter(1, microsoft.sql.Types.VARIANT); - cs.registerOutParameter(2, microsoft.sql.Types.VARIANT); - cs.setObject(3, col2Value, microsoft.sql.Types.VARIANT); + 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)); From b0def577094708f45ada64894838542e08ffe682 Mon Sep 17 00:00:00 2001 From: v-afrafi Date: Fri, 31 Mar 2017 14:46:16 -0700 Subject: [PATCH 11/13] removed the unwanted build.xml file that was added after merge conflict. --- build.xml | 106 ---- .../sqlserver/jdbc/PLPInputStream.java.orig | 534 ------------------ 2 files changed, 640 deletions(-) delete mode 100644 build.xml delete mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/PLPInputStream.java.orig diff --git a/build.xml b/build.xml deleted file mode 100644 index f6c5ed960e..0000000000 --- a/build.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - ----- Building sqljdbc Project ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/PLPInputStream.java.orig b/src/main/java/com/microsoft/sqlserver/jdbc/PLPInputStream.java.orig deleted file mode 100644 index 87f6d8dced..0000000000 --- a/src/main/java/com/microsoft/sqlserver/jdbc/PLPInputStream.java.orig +++ /dev/null @@ -1,534 +0,0 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.jdbc; - -import java.io.ByteArrayInputStream; -import java.io.IOException; - -/** - * PLPInputStream is an InputStream implementation that reads from a TDS PLP stream. - * - * Note PLP stands for Partially Length-prefixed Bytes. TDS 7.2 introduced this new streaming format for streaming of large types such as - * varchar(max), nvarchar(max), varbinary(max) and XML. - * - * See TDS specification, 6.3.3 Datatype Dependant Data Streams: Partially Length-prefixed Bytes for more details on the PLP format. - */ - -class PLPInputStream extends BaseInputStream { - static final long PLP_NULL = 0xFFFFFFFFFFFFFFFFL; - static final long UNKNOWN_PLP_LEN = 0xFFFFFFFFFFFFFFFEL; - static final int PLP_TERMINATOR = 0x00000000; - private final static byte[] EMPTY_PLP_BYTES = new byte[0]; - - // Stated length of the PLP stream payload; -1 if unknown length. - int payloadLength; - - private static final int PLP_EOS = -1; - private int currentChunkRemain; - - private int markedChunkRemain; - private int leftOverReadLimit = 0; - - private byte[] oneByteArray = new byte[1]; - - /** - * Non-destructive method for checking whether a PLP value at the current TDSReader location is null. - */ - final static boolean isNull(TDSReader tdsReader) throws SQLServerException { - TDSReaderMark mark = tdsReader.mark(); - //Temporary stream cannot get closes, since it closes the main stream. - try { - return null == PLPInputStream.makeTempStream(tdsReader, false, null); - } - finally { -<<<<<<< HEAD - - if (null != tdsReader) - - tdsReader.reset(mark); -======= - tdsReader.reset(mark); ->>>>>>> 5885874602cae79c303dd9ab39825c61fce0d0ef - } - } - - /** - * Create a new input stream. - * - * @param tdsReader - * TDS reader pointing at the start of the PLP data - * @param discardValue - * boolean to represent if base input stream is adaptive and is streaming - * @param dtv - * DTV implementation for values set from the TDS response stream. - * @return PLPInputStream that is created - * @throws SQLServerException - * when an error occurs - */ - final static PLPInputStream makeTempStream(TDSReader tdsReader, - boolean discardValue, - ServerDTVImpl dtv) throws SQLServerException { - return makeStream(tdsReader, discardValue, discardValue, dtv); - } - - final static PLPInputStream makeStream(TDSReader tdsReader, - InputStreamGetterArgs getterArgs, - ServerDTVImpl dtv) throws SQLServerException { - PLPInputStream is = makeStream(tdsReader, getterArgs.isAdaptive, getterArgs.isStreaming, dtv); - if (null != is) - is.setLoggingInfo(getterArgs.logContext); - return is; - } - - private static PLPInputStream makeStream(TDSReader tdsReader, - boolean isAdaptive, - boolean isStreaming, - ServerDTVImpl dtv) throws SQLServerException { - // Read total length of PLP stream. - long payloadLength = tdsReader.readLong(); - - // If length is PLP_NULL, then return a null PLP value. - if (PLP_NULL == payloadLength) - return null; - - return new PLPInputStream(tdsReader, payloadLength, isAdaptive, isStreaming, dtv); - } - - /** - * Initializes the input stream. - */ - PLPInputStream(TDSReader tdsReader, - long statedPayloadLength, - boolean isAdaptive, - boolean isStreaming, - ServerDTVImpl dtv) throws SQLServerException { - super(tdsReader, isAdaptive, isStreaming, dtv); - this.payloadLength = (UNKNOWN_PLP_LEN != statedPayloadLength) ? ((int) statedPayloadLength) : -1; - this.currentChunkRemain = this.markedChunkRemain = 0; - } - - /** - * Helper function to convert the entire PLP stream into a contiguous byte array. This call is inefficient (in terms of memory usage and run time) - * for very large PLPs. Use it only if a contiguous byte array is required. - */ - byte[] getBytes() throws SQLServerException { - byte[] value; - - // The following 0-byte read just ensures that the number of bytes - // remaining in the current chunk is known. - readBytesInternal(null, 0, 0); - - if (PLP_EOS == currentChunkRemain) { - value = EMPTY_PLP_BYTES; - } - else { - // If the PLP payload length is known, allocate the final byte array now. - // Otherwise, start with the size of the first chunk. Additional chunks - // will cause the array to be reallocated & copied. - value = new byte[(-1 != payloadLength) ? payloadLength : currentChunkRemain]; - - int bytesRead = 0; - while (PLP_EOS != currentChunkRemain) { - // If the current byte array isn't large enough to hold - // the contents of the current chunk, then make it larger. - if (value.length == bytesRead) { - byte[] newValue = new byte[bytesRead + currentChunkRemain]; - System.arraycopy(value, 0, newValue, 0, bytesRead); - value = newValue; - } - - bytesRead += readBytesInternal(value, bytesRead, currentChunkRemain); - } - } - - // Always close the stream after retrieving it - try { - close(); - } - catch (IOException e) { - SQLServerException.makeFromDriverError(null, null, e.getMessage(), null, true); - } - - return value; - } - - /** - * Skips over and discards n bytes of data from this input stream. - * - * @param n - * the number of bytes to be skipped. - * @return the actual number of bytes skipped. - * @exception IOException - * if an I/O error occurs. - */ - public long skip(long n) throws IOException { - checkClosed(); - if (n < 0) - return 0L; - if (n > Integer.MAX_VALUE) - n = Integer.MAX_VALUE; - - long bytesread = readBytes(null, 0, (int) n); - - if (-1 == bytesread) - return 0; - else - return bytesread; - } - - /** - * Returns the number of bytes that can be read (or skipped over) from this input stream without blocking by the next caller of a method for this - * input stream. - * - * @return the actual number of bytes available. - * @exception IOException - * if an I/O error occurs. - */ - public int available() throws IOException { - checkClosed(); - try { - - // The following 0-byte read just ensures that the number of bytes - // remaining in the current chunk is known. - if (0 == currentChunkRemain) - readBytesInternal(null, 0, 0); - - if (PLP_EOS == currentChunkRemain) - return 0; - - // Return the lesser of the number of bytes available for reading - // from the underlying TDSReader and the number of bytes left in - // the current chunk. - int available = tdsReader.available(); - if (available > currentChunkRemain) - available = currentChunkRemain; - - return available; - } - catch (SQLServerException e) { - throw new IOException(e.getMessage()); - } - - } - - /** - * Reads the next byte of data from the input stream. - * - * @return the byte read or -1 meaning no more bytes. - * @exception IOException - * if an I/O error occurs. - */ - public int read() throws IOException { - checkClosed(); - - if (-1 != readBytes(oneByteArray, 0, 1)) - return oneByteArray[0] & 0xFF; - return -1; - } - - /** - * Reads available data into supplied byte array. - * - * @param b - * array of bytes to fill. - * @return the number of bytes read or 0 meaning no bytes read. - * @exception IOException - * if an I/O error occurs. - */ - public int read(byte[] b) throws IOException { - // If b is null, a NullPointerException is thrown. - if (null == b) - throw new NullPointerException(); - - checkClosed(); - - return readBytes(b, 0, b.length); - } - - /** - * Reads available data into supplied byte array. - * - * @param b - * array of bytes to fill. - * @param offset - * the offset into array b where to start writing. - * @param maxBytes - * the max number of bytes to write into b. - * @return the number of bytes read or 0 meaning no bytes read. - * @exception IOException - * if an I/O error occurs. - */ - public int read(byte b[], - int offset, - int maxBytes) throws IOException { - // If b is null, a NullPointerException is thrown. - if (null == b) - throw new NullPointerException(); - - // Verify offset and maxBytes against target buffer if we're reading (as opposed to skipping). - // If offset is negative, or maxBytes is negative, or offset+maxBytes - // is greater than the length of the array b, then an IndexOutOfBoundsException is thrown. - if (offset < 0 || maxBytes < 0 || offset + maxBytes > b.length) - throw new IndexOutOfBoundsException(); - - checkClosed(); - - return readBytes(b, offset, maxBytes); - } - - /** - * Reads available data into supplied byte array b. - * - * @param b - * array of bytes to fill. If b is null, method will skip over data. - * @param offset - * the offset into array b where to start writing. - * @param maxBytes - * the max number of bytes to write into b. - * @return the number of bytes read or 0 meaning no bytes read or -1 meaning EOS. - * @exception IOException - * if an I/O error occurs. - */ - int readBytes(byte[] b, - int offset, - int maxBytes) throws IOException { - // If maxBytes is zero, then no bytes are read and 0 is returned - // This must be done here rather than in readBytesInternal since a 0-byte read - // there may return -1 at EOS. - if (0 == maxBytes) - return 0; - - try { - return readBytesInternal(b, offset, maxBytes); - } - catch (SQLServerException e) { - throw new IOException(e.getMessage()); - } - } - - private int readBytesInternal(byte b[], - int offset, - int maxBytes) throws SQLServerException { - // If we're at EOS, say so. - // Note: For back compat, this special case needs to always be handled - // before checking user-supplied arguments below. - if (PLP_EOS == currentChunkRemain) - return -1; - - // Save off the current TDSReader position, wherever it is, and start reading - // from where we left off last time. - - int bytesRead = 0; - for (;;) { - // Check that we have bytes left to read from the current chunk. - // If not then figure out the size of the next chunk or - // determine that we have reached the end of the stream. - if (0 == currentChunkRemain) { - currentChunkRemain = (int) tdsReader.readUnsignedInt(); - assert currentChunkRemain >= 0; - if (0 == currentChunkRemain) { - currentChunkRemain = PLP_EOS; - break; - } - } - - if (bytesRead == maxBytes) - break; - - // Now we know there are bytes to be read in the current chunk. - // Further limit the max number of bytes we can read to whatever - // remains in the current chunk. - int bytesToRead = maxBytes - bytesRead; - if (bytesToRead > currentChunkRemain) - bytesToRead = currentChunkRemain; - - // Skip/Read as many bytes as we can, given the constraints. - if (null == b) - tdsReader.skip(bytesToRead); - else - tdsReader.readBytes(b, offset + bytesRead, bytesToRead); - - bytesRead += bytesToRead; - currentChunkRemain -= bytesToRead; - } - - if (bytesRead > 0) { - if (isReadLimitSet && leftOverReadLimit > 0) { - leftOverReadLimit = leftOverReadLimit - bytesRead; - if (leftOverReadLimit < 0) - clearCurrentMark(); - } - return bytesRead; - } - - if (PLP_EOS == currentChunkRemain) - return -1; - - return 0; - } - - /** - * Marks the current position in this input stream. - * - * @param readlimit - * the number of bytes to hold (this implementation ignores this). - */ - public void mark(int readLimit) { - // Save off current position and how much of the current chunk remains - // cant throw if the tdsreader is null - if (null != tdsReader && readLimit > 0) { - currentMark = tdsReader.mark(); - markedChunkRemain = currentChunkRemain; - leftOverReadLimit = readLimit; - setReadLimit(readLimit); - } - } - - /** - * Closes the stream releasing all resources held. - * - * @exception IOException - * if an I/O error occurs. - */ - public void close() throws IOException { - if (null == tdsReader) - return; - - while (skip(tdsReader.getConnection().getTDSPacketSize()) != 0) - ; - // Release ref to tdsReader and parentRS here, shut down stream state. - closeHelper(); - } - - /** - * Resets stream to saved mark position. - * - * @exception IOException - * if an I/O error occurs. - */ - public void reset() throws IOException { - resetHelper(); - leftOverReadLimit = readLimit; - currentChunkRemain = markedChunkRemain; - } -} - -/** - * Implements an XML binary stream with BOM header. - * - * Class extends a normal PLPInputStream class and prepends the XML BOM (0xFFFE) token then steps out of the way and forwards the rest of the - * InputStream calls to the super class PLPInputStream. - */ -final class PLPXMLInputStream extends PLPInputStream { - // XML BOM header (the first two header bytes sent to caller). - private final static byte[] xmlBOM = {(byte) 0xFF, (byte) 0xFE}; - private final ByteArrayInputStream bomStream = new ByteArrayInputStream(xmlBOM); - - final static PLPXMLInputStream makeXMLStream(TDSReader tdsReader, - InputStreamGetterArgs getterArgs, - ServerDTVImpl dtv) throws SQLServerException { - // Read total length of PLP stream. - long payloadLength = tdsReader.readLong(); - - // If length is PLP_NULL, then return a null PLP value. - if (PLP_NULL == payloadLength) - return null; - - PLPXMLInputStream is = new PLPXMLInputStream(tdsReader, payloadLength, getterArgs, dtv); - if (null != is) - is.setLoggingInfo(getterArgs.logContext); - - return is; - } - - PLPXMLInputStream(TDSReader tdsReader, - long statedPayloadLength, - InputStreamGetterArgs getterArgs, - ServerDTVImpl dtv) throws SQLServerException { - super(tdsReader, statedPayloadLength, getterArgs.isAdaptive, getterArgs.isStreaming, dtv); - } - - public void close() throws IOException { - super.close(); - } - - int readBytes(byte[] b, - int offset, - int maxBytes) throws IOException { - assert offset >= 0; - assert maxBytes >= 0; - // If maxBytes is zero, then no bytes are read and 0 is returned. - if (0 == maxBytes) - return 0; - - int bytesRead = 0; - int xmlBytesRead = 0; - - // Read/Skip BOM bytes first. When all BOM bytes have been consumed ... - if (null == b) { - for (int bomBytesSkipped = 0; bytesRead < maxBytes - && 0 != (bomBytesSkipped = (int) bomStream.skip(maxBytes - bytesRead)); bytesRead += bomBytesSkipped) - ; - } - else { - for (int bomBytesRead = 0; bytesRead < maxBytes - && -1 != (bomBytesRead = bomStream.read(b, offset + bytesRead, maxBytes - bytesRead)); bytesRead += bomBytesRead) - ; - } - - // ... then read/skip bytes from the underlying PLPInputStream - for (; bytesRead < maxBytes && -1 != (xmlBytesRead = super.readBytes(b, offset + bytesRead, maxBytes - bytesRead)); bytesRead += xmlBytesRead) - ; - - if (bytesRead > 0) - return bytesRead; - - // No bytes read - should have been EOF since 0-byte reads are handled above - assert -1 == xmlBytesRead; - return -1; - } - - public void mark(int readLimit) { - bomStream.mark(xmlBOM.length); - super.mark(readLimit); - } - - public void reset() throws IOException { - bomStream.reset(); - super.reset(); - } - - /** - * Helper function to convert the entire PLP stream into a contiguous byte array. This call is inefficient (in terms of memory usage and run time) - * for very large PLPs. Use it only if a contiguous byte array is required. - */ - byte[] getBytes() throws SQLServerException { - // Look to see if the BOM has been read - byte[] bom = new byte[2]; - try { - int bytesread = bomStream.read(bom); - byte[] valueWithoutBOM = super.getBytes(); - - if (bytesread > 0) { - assert 2 == bytesread; - byte[] valueWithBOM = new byte[valueWithoutBOM.length + bytesread]; - System.arraycopy(bom, 0, valueWithBOM, 0, bytesread); - System.arraycopy(valueWithoutBOM, 0, valueWithBOM, bytesread, valueWithoutBOM.length); - return valueWithBOM; - } - else - return valueWithoutBOM; - } - catch (IOException e) { - SQLServerException.makeFromDriverError(null, null, e.getMessage(), null, true); - } - - return null; - } -} From d0b0498f2541abbe4edc63bf739865fe84981901 Mon Sep 17 00:00:00 2001 From: v-afrafi Date: Fri, 31 Mar 2017 14:51:59 -0700 Subject: [PATCH 12/13] formatting code and changing sql_variant JDBCType to uppercase --- .../java/com/microsoft/sqlserver/jdbc/DataTypes.java | 11 +++++------ .../java/com/microsoft/sqlserver/jdbc/IOBuffer.java | 4 ---- .../java/com/microsoft/sqlserver/jdbc/Parameter.java | 2 +- src/main/java/com/microsoft/sqlserver/jdbc/dtv.java | 11 +++++------ 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java index 49703ee2b0..148d643a5a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java @@ -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.Sql_Variant, "sql_variant", JDBCType.CHAR), + SQL_VARIANT (Category.SQL_VARIANT, "sql_variant", JDBCType.CHAR), 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, - Sql_Variant, + SQL_VARIANT, XML } @@ -360,7 +360,7 @@ enum GetterConversion JDBCType.Category.BINARY, JDBCType.Category.CHARACTER)), Sql_Variant ( - SSType.Category.Sql_Variant, + SSType.Category.SQL_VARIANT, EnumSet.of( JDBCType.Category.CHARACTER, JDBCType.Category.Sql_Variant)); @@ -848,8 +848,7 @@ enum JDBCType 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"), -// Variant (Category.Variant, microsoft.sql.Types.VARIANT, "java.lang.String"); - Sql_Variant (Category.Sql_Variant, microsoft.sql.Types.SQL_VARIANT, "java.lang.String"); + SQL_VARIANT (Category.Sql_Variant, microsoft.sql.Types.SQL_VARIANT, "java.lang.String"); final Category category; @@ -1196,7 +1195,7 @@ enum UpdaterConversion { SSType.Category.LONG_CHARACTER, SSType.Category.NCHARACTER, SSType.Category.LONG_NCHARACTER, - SSType.Category.Sql_Variant)), + SSType.Category.SQL_VARIANT)), DATE ( JDBCType.Category.DATE, diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 7c3c11e765..d5011c7c88 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -6496,10 +6496,6 @@ final Object readDecimal(int valueLength, return DDC.convertBigDecimalToObject(Util.readBigDecimal(valueBytes, valueLength, typeInfo.getScale()), jdbcType, streamType); } -// final Object readSqlVariant() { -// -// } - final Object readMoney(int valueLength, JDBCType jdbcType, StreamType streamType) throws SQLServerException { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java index ab67331d87..9366b66b99 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java @@ -884,7 +884,7 @@ else if ((null != jdbcTypeSetByUser) && ((jdbcTypeSetByUser == JDBCType.NVARCHAR case GUID: param.typeDefinition = SSType.GUID.toString(); break; - case Sql_Variant: + case SQL_VARIANT: param.typeDefinition = SSType.SQL_VARIANT.toString(); break; default: diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java index 62b3d04c10..b8dac31b8a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java @@ -1596,7 +1596,7 @@ final void executeOp(DTVExecuteOp op) throws SQLServerException { case STRUCT: unsupportedConversion = true; break; - case Sql_Variant: + case SQL_VARIANT: op.execute(this, (SqlVariant) null); break; @@ -1621,7 +1621,7 @@ final void executeOp(DTVExecuteOp op) throws SQLServerException { byte[] bArray = Util.asGuidByteArray((UUID) value); op.execute(this, bArray); } - else if (jdbcType.Sql_Variant == jdbcType){ + else if (jdbcType.SQL_VARIANT == jdbcType){ op.execute(this, String.valueOf(value)); } else { @@ -2308,16 +2308,15 @@ else if (null != collation } } - /* (non-Javadoc) + /* + * (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 { - // TODO Auto-generated method stub - } - } From 8733aedf0d560421fcee578d32a603a66fe21bc5 Mon Sep 17 00:00:00 2001 From: v-afrafi Date: Fri, 31 Mar 2017 15:15:06 -0700 Subject: [PATCH 13/13] code formatting. --- .../java/com/microsoft/sqlserver/jdbc/DataTypes.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java index 148d643a5a..ecee806577 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/DataTypes.java @@ -363,7 +363,7 @@ enum GetterConversion SSType.Category.SQL_VARIANT, EnumSet.of( JDBCType.Category.CHARACTER, - JDBCType.Category.Sql_Variant)); + JDBCType.Category.SQL_VARIANT)); private final SSType.Category from; private final EnumSet to; @@ -848,7 +848,7 @@ enum JDBCType 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"), - SQL_VARIANT (Category.Sql_Variant, microsoft.sql.Types.SQL_VARIANT, "java.lang.String"); + SQL_VARIANT (Category.SQL_VARIANT, microsoft.sql.Types.SQL_VARIANT, "java.lang.String"); final Category category; @@ -897,8 +897,7 @@ enum Category { UNKNOWN, TVP, GUID, - // Variant; - Sql_Variant; + SQL_VARIANT; } // This SetterConversion enum is based on the Category enum @@ -991,7 +990,7 @@ enum SetterConversion { JDBCType.Category.LONG_CHARACTER, JDBCType.Category.NCHARACTER, JDBCType.Category.LONG_NCHARACTER, - JDBCType.Category.Sql_Variant)), + JDBCType.Category.SQL_VARIANT)), DATE ( JDBCType.Category.DATE,