in src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java [5098:5374]
private void writeInternalTVPRowValues(JDBCType jdbcType, String currentColumnStringValue, Object currentObject,
Map.Entry<Integer, SQLServerMetaData> columnPair, boolean isSqlVariant) throws SQLServerException {
boolean isShortValue, isNull;
int dataLength;
switch (jdbcType) {
case BIGINT:
if (null == currentColumnStringValue)
writeByte((byte) 0);
else {
if (isSqlVariant) {
writeTVPSqlVariantHeader(10, TDSType.INT8.byteValue(), (byte) 0);
} else {
writeByte((byte) 8);
}
writeLong(Long.valueOf(currentColumnStringValue).longValue());
}
break;
case BIT:
if (null == currentColumnStringValue)
writeByte((byte) 0);
else {
if (isSqlVariant)
writeTVPSqlVariantHeader(3, TDSType.BIT1.byteValue(), (byte) 0);
else
writeByte((byte) 1);
writeByte((byte) (Boolean.valueOf(currentColumnStringValue).booleanValue() ? 1 : 0));
}
break;
case INTEGER:
if (null == currentColumnStringValue)
writeByte((byte) 0);
else {
if (!isSqlVariant)
writeByte((byte) 4);
else
writeTVPSqlVariantHeader(6, TDSType.INT4.byteValue(), (byte) 0);
writeInt(Integer.valueOf(currentColumnStringValue).intValue());
}
break;
case SMALLINT:
case TINYINT:
if (null == currentColumnStringValue)
writeByte((byte) 0);
else {
if (isSqlVariant) {
writeTVPSqlVariantHeader(6, TDSType.INT4.byteValue(), (byte) 0);
writeInt(Integer.valueOf(currentColumnStringValue));
} else {
writeByte((byte) 2); // length of datatype
writeShort(Short.valueOf(currentColumnStringValue).shortValue());
}
}
break;
case DECIMAL:
case NUMERIC:
if (null == currentColumnStringValue)
writeByte((byte) 0);
else {
if (isSqlVariant) {
writeTVPSqlVariantHeader(21, TDSType.DECIMALN.byteValue(), (byte) 2);
writeByte((byte) 38); // scale (byte)variantType.getScale()
writeByte((byte) 4); // scale (byte)variantType.getScale()
} else {
writeByte((byte) TDSWriter.BIGDECIMAL_MAX_LENGTH); // maximum length
}
BigDecimal bdValue = new BigDecimal(currentColumnStringValue);
/*
* setScale of all BigDecimal value based on metadata as scale is not sent separately for individual
* value. Use the rounding used in Server. Say, for BigDecimal("0.1"), if scale in metdadata is 0,
* then ArithmeticException would be thrown if RoundingMode is not set
*/
bdValue = bdValue.setScale(columnPair.getValue().scale, RoundingMode.HALF_UP);
byte[] valueBytes = DDC.convertBigDecimalToBytes(bdValue, bdValue.scale());
// 1-byte for sign and 16-byte for integer
byte[] byteValue = new byte[17];
// removing the precision and scale information from the valueBytes array
System.arraycopy(valueBytes, 2, byteValue, 0, valueBytes.length - 2);
writeBytes(byteValue);
}
break;
case DOUBLE:
if (null == currentColumnStringValue)
writeByte((byte) 0); // len of data bytes
else {
if (isSqlVariant) {
writeTVPSqlVariantHeader(10, TDSType.FLOAT8.byteValue(), (byte) 0);
writeDouble(Double.valueOf(currentColumnStringValue));
break;
}
writeByte((byte) 8); // len of data bytes
long bits = Double.doubleToLongBits(Double.valueOf(currentColumnStringValue).doubleValue());
long mask = 0xFF;
int nShift = 0;
for (int i = 0; i < 8; i++) {
writeByte((byte) ((bits & mask) >> nShift));
nShift += 8;
mask = mask << 8;
}
}
break;
case FLOAT:
case REAL:
if (null == currentColumnStringValue)
writeByte((byte) 0);
else {
if (isSqlVariant) {
writeTVPSqlVariantHeader(6, TDSType.FLOAT4.byteValue(), (byte) 0);
writeInt(Float.floatToRawIntBits(Float.valueOf(currentColumnStringValue).floatValue()));
} else {
writeByte((byte) 4);
writeInt(Float.floatToRawIntBits(Float.valueOf(currentColumnStringValue).floatValue()));
}
}
break;
case DATE:
case TIME:
case TIMESTAMP:
case DATETIMEOFFSET:
case DATETIME:
case SMALLDATETIME:
case TIMESTAMP_WITH_TIMEZONE:
case TIME_WITH_TIMEZONE:
case CHAR:
case VARCHAR:
case NCHAR:
case NVARCHAR:
case LONGVARCHAR:
case LONGNVARCHAR:
case SQLXML:
isShortValue = (2L * columnPair.getValue().precision) <= DataTypes.SHORT_VARTYPE_MAX_BYTES;
isNull = (null == currentColumnStringValue);
dataLength = isNull ? 0 : currentColumnStringValue.length() * 2;
if (!isShortValue) {
// check null
if (isNull) {
// Null header for v*max types is 0xFFFFFFFFFFFFFFFF.
writeLong(0xFFFFFFFFFFFFFFFFL);
} else if (isSqlVariant) {
// for now we send as bigger type, but is sendStringParameterAsUnicode is set to false we can't
// send nvarchar
// since we are writing as nvarchar we need to write as tdstype.bigvarchar value because if we
// want to supprot varchar(8000) it becomes as nvarchar, 8000*2 therefore we should send as
// longvarchar,
// but we cannot send more than 8000 cause sql_variant datatype in sql server does not support
// it.
// then throw exception if user is sending more than that
if (dataLength > 2 * DataTypes.SHORT_VARTYPE_MAX_BYTES) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_invalidStringValue"));
throw new SQLServerException(null, form.format(new Object[] {}), null, 0, false);
}
int length = currentColumnStringValue.length();
writeTVPSqlVariantHeader(9 + length, TDSType.BIGVARCHAR.byteValue(), (byte) 0x07);
SQLCollation col = con.getDatabaseCollation();
// write collation for sql variant
writeInt(col.getCollationInfo());
writeByte((byte) col.getCollationSortID());
writeShort((short) (length));
writeBytes(currentColumnStringValue.getBytes());
break;
}
else if (DataTypes.UNKNOWN_STREAM_LENGTH == dataLength)
// Append v*max length.
// UNKNOWN_PLP_LEN is 0xFFFFFFFFFFFFFFFE
writeLong(0xFFFFFFFFFFFFFFFEL);
else
// For v*max types with known length, length is <totallength8><chunklength4>
writeLong(dataLength);
if (!isNull) {
if (dataLength > 0) {
writeInt(dataLength);
writeString(currentColumnStringValue);
}
// Send the terminator PLP chunk.
writeInt(0);
}
} else {
if (isNull)
writeShort((short) -1); // actual len
else {
if (isSqlVariant) {
// for now we send as bigger type, but is sendStringParameterAsUnicoe is set to false we
// can't send nvarchar
// check for this
int length = currentColumnStringValue.length() * 2;
writeTVPSqlVariantHeader(9 + length, TDSType.NVARCHAR.byteValue(), (byte) 7);
SQLCollation col = con.getDatabaseCollation();
// write collation for sql variant
writeInt(col.getCollationInfo());
writeByte((byte) col.getCollationSortID());
int stringLength = currentColumnStringValue.length();
byte[] typevarlen = new byte[2];
typevarlen[0] = (byte) (2 * stringLength & 0xFF);
typevarlen[1] = (byte) ((2 * stringLength >> 8) & 0xFF);
writeBytes(typevarlen);
writeString(currentColumnStringValue);
break;
} else {
writeShort((short) dataLength);
writeString(currentColumnStringValue);
}
}
}
break;
case BINARY:
case VARBINARY:
case LONGVARBINARY:
// Handle conversions as done in other types.
isShortValue = columnPair.getValue().precision <= DataTypes.SHORT_VARTYPE_MAX_BYTES;
isNull = (null == currentObject);
if (currentObject instanceof String)
dataLength = ParameterUtils.HexToBin(currentObject.toString()).length;
else
dataLength = isNull ? 0 : ((byte[]) currentObject).length;
if (!isShortValue) {
// check null
if (isNull)
// Null header for v*max types is 0xFFFFFFFFFFFFFFFF.
writeLong(0xFFFFFFFFFFFFFFFFL);
else if (DataTypes.UNKNOWN_STREAM_LENGTH == dataLength)
// Append v*max length.
// UNKNOWN_PLP_LEN is 0xFFFFFFFFFFFFFFFE
writeLong(0xFFFFFFFFFFFFFFFEL);
else
// For v*max types with known length, length is <totallength8><chunklength4>
writeLong(dataLength);
if (!isNull) {
if (dataLength > 0) {
writeInt(dataLength);
if (currentObject instanceof String)
writeBytes(ParameterUtils.HexToBin(currentObject.toString()));
else
writeBytes((byte[]) currentObject);
}
// Send the terminator PLP chunk.
writeInt(0);
}
} else {
if (isNull)
writeShort((short) -1); // actual len
else {
writeShort((short) dataLength);
if (currentObject instanceof String)
writeBytes(ParameterUtils.HexToBin(currentObject.toString()));
else
writeBytes((byte[]) currentObject);
}
}
break;
case SQL_VARIANT:
boolean isShiloh = (8 >= con.getServerMajorVersion());
if (isShiloh) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_SQLVariantSupport"));
throw new SQLServerException(null, form.format(new Object[] {}), null, 0, false);
}
JDBCType internalJDBCType;
JavaType javaType = JavaType.of(currentObject);
internalJDBCType = javaType.getJDBCType(SSType.UNKNOWN, jdbcType);
writeInternalTVPRowValues(internalJDBCType, currentColumnStringValue, currentObject, columnPair, true);
break;
default:
assert false : "Unexpected JDBC type " + jdbcType.toString();
}
}