in src/main/java/com/microsoft/sqlserver/jdbc/dtv.java [3428:3660]
Object denormalizedValue(byte[] decryptedValue, JDBCType jdbcType, TypeInfo baseTypeInfo, SQLServerConnection con,
InputStreamGetterArgs streamGetterArgs, byte normalizeRuleVersion, Calendar cal) throws SQLServerException {
if (0x01 != normalizeRuleVersion) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_UnsupportedNormalizationVersionAE"));
throw new SQLServerException(form.format(new Object[] {normalizeRuleVersion, 1}), null, 0, null);
}
if (aeLogger.isLoggable(java.util.logging.Level.FINE)) {
aeLogger.fine("Denormalizing decrypted data based on its SQL Server type(" + baseTypeInfo.getSSType()
+ ") and JDBC type(" + jdbcType + ").");
}
SSType baseSSType = baseTypeInfo.getSSType();
switch (baseSSType) {
case CHAR:
case VARCHAR:
case NCHAR:
case NVARCHAR:
case VARCHARMAX:
case NVARCHARMAX: {
try {
String strVal = new String(decryptedValue, 0, decryptedValue.length,
(null == baseTypeInfo.getCharset()) ? con.getDatabaseCollation().getCharset()
: baseTypeInfo.getCharset());
if ((SSType.CHAR == baseSSType) || (SSType.NCHAR == baseSSType)) {
// Right pad the string for CHAR types.
StringBuilder sb = new StringBuilder(strVal);
int padLength = baseTypeInfo.getPrecision() - strVal.length();
for (int i = 0; i < padLength; i++) {
sb.append(' ');
}
strVal = sb.toString();
}
return DDC.convertStringToObject(strVal, baseTypeInfo.getCharset(), jdbcType,
streamGetterArgs.streamType);
} catch (IllegalArgumentException e) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_errorConvertingValue"));
throw new SQLServerException(form.format(new Object[] {baseSSType, jdbcType}), null, 0, e);
} catch (UnsupportedEncodingException e) {
// Important: we should not pass the exception here as it displays the data.
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unsupportedEncoding"));
throw new SQLServerException(form.format(new Object[] {baseTypeInfo.getCharset()}), null, 0, e);
}
}
case BIT:
case TINYINT:
case SMALLINT:
case INTEGER:
case BIGINT: {
// If data is encrypted, then these types are normalized to BIGINT. Need to denormalize here.
if (8 != decryptedValue.length) {
// Integer datatypes are normalized to bigint for AE.
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NormalizationErrorAE"));
throw new SQLServerException(form.format(new Object[] {baseSSType}), null, 0, null);
}
return DDC.convertLongToObject(Util.readLong(decryptedValue, 0), jdbcType, baseSSType,
streamGetterArgs.streamType);
}
case REAL:
case FLOAT: {
// JDBC driver does not normalize real to float.
if (8 == decryptedValue.length) {
return DDC.convertDoubleToObject(
ByteBuffer.wrap(decryptedValue).order(ByteOrder.LITTLE_ENDIAN).getDouble(),
JDBCType.VARBINARY == jdbcType ? baseSSType.getJDBCType() : jdbcType, // use jdbc type from
// baseTypeInfo if
// using
// getObject()
streamGetterArgs.streamType);
} else if (4 == decryptedValue.length) {
return DDC.convertFloatToObject(
ByteBuffer.wrap(decryptedValue).order(ByteOrder.LITTLE_ENDIAN).getFloat(),
JDBCType.VARBINARY == jdbcType ? baseSSType.getJDBCType() : jdbcType, // use jdbc type from
// baseTypeInfo if
// using
// getObject()
streamGetterArgs.streamType);
} else {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NormalizationErrorAE"));
throw new SQLServerException(form.format(new Object[] {baseSSType}), null, 0, null);
}
}
case SMALLMONEY: {
return DDC.convertMoneyToObject(new BigDecimal(BigInteger.valueOf(Util.readInt(decryptedValue, 4)), 4),
JDBCType.VARBINARY == jdbcType ? baseSSType.getJDBCType() : jdbcType, // use jdbc type from
// baseTypeInfo if using
// getObject()
streamGetterArgs.streamType, 4);
}
case MONEY: {
BigInteger bi = BigInteger.valueOf(((long) Util.readInt(decryptedValue, 0) << 32)
| (Util.readInt(decryptedValue, 4) & 0xFFFFFFFFL));
return DDC.convertMoneyToObject(new BigDecimal(bi, 4),
JDBCType.VARBINARY == jdbcType ? baseSSType.getJDBCType() : jdbcType, // use
// jdbc
// type
// from
// baseTypeInfo
// if
// using
// getObject()
streamGetterArgs.streamType, 8);
}
case NUMERIC:
case DECIMAL: {
return DDC.convertBigDecimalToObject(
Util.readBigDecimal(decryptedValue, decryptedValue.length, baseTypeInfo.getScale()),
JDBCType.VARBINARY == jdbcType ? baseSSType.getJDBCType() : jdbcType, // use jdbc type from
// baseTypeInfo if using
// getObject()
streamGetterArgs.streamType);
}
case BINARY:
case VARBINARY:
case VARBINARYMAX: {
return DDC.convertBytesToObject(decryptedValue, jdbcType, baseTypeInfo);
}
case DATE: {
// get the number of days !! Size should be 3
if (3 != decryptedValue.length) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NormalizationErrorAE"));
throw new SQLServerException(form.format(new Object[] {baseSSType}), null, 0, null);
}
// Getting number of days
// Following Lines of code copied from IOBuffer.readDaysIntoCE as we
// cannot reuse method
int daysIntoCE = getDaysIntoCE(decryptedValue, baseSSType);
return DDC.convertTemporalToObject(jdbcType, baseSSType, cal, daysIntoCE, 0, 0);
}
case TIME: {
long localNanosSinceMidnight = readNanosSinceMidnightAE(decryptedValue, baseTypeInfo.getScale(),
baseSSType);
return DDC.convertTemporalToObject(jdbcType, SSType.TIME, cal, 0, localNanosSinceMidnight,
baseTypeInfo.getScale());
}
case DATETIME2: {
if (8 != decryptedValue.length) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NormalizationErrorAE"));
throw new SQLServerException(form.format(new Object[] {baseSSType}), null, 0, null);
}
// Last three bytes are for date and remaining for time
int dateOffset = decryptedValue.length - 3;
byte[] timePortion = new byte[dateOffset];
byte[] datePortion = new byte[3];
System.arraycopy(decryptedValue, 0, timePortion, 0, dateOffset);
System.arraycopy(decryptedValue, dateOffset, datePortion, 0, 3);
long localNanosSinceMidnight = readNanosSinceMidnightAE(timePortion, baseTypeInfo.getScale(),
baseSSType);
int daysIntoCE = getDaysIntoCE(datePortion, baseSSType);
// Convert the DATETIME2 value to the desired Java type.
return DDC.convertTemporalToObject(jdbcType, SSType.DATETIME2, cal, daysIntoCE, localNanosSinceMidnight,
baseTypeInfo.getScale());
}
case SMALLDATETIME: {
if (4 != decryptedValue.length) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NormalizationErrorAE"));
throw new SQLServerException(form.format(new Object[] {baseSSType}), null, 0, null);
}
// SQL smalldatetime has less precision. It stores 2 bytes
// for the days since SQL Base Date and 2 bytes for minutes
// after midnight.
return DDC.convertTemporalToObject(jdbcType, SSType.DATETIME, cal,
Util.readUnsignedShort(decryptedValue, 0),
Util.readUnsignedShort(decryptedValue, 2) * 60L * 1000L, 0);
}
case DATETIME: {
int ticksSinceMidnight = (Util.readInt(decryptedValue, 4) * 10 + 1) / 3;
if (8 != decryptedValue.length || Integer.MAX_VALUE < ticksSinceMidnight) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NormalizationErrorAE"));
throw new SQLServerException(form.format(new Object[] {baseSSType}), null, 0, null);
}
// SQL datetime is 4 bytes for days since SQL Base Date
// (January 1, 1900 00:00:00 GMT) and 4 bytes for
// the number of three hundredths (1/300) of a second since midnight.
return DDC.convertTemporalToObject(jdbcType, SSType.DATETIME, cal, Util.readInt(decryptedValue, 0),
ticksSinceMidnight, 0);
}
case DATETIMEOFFSET: {
// Last 5 bytes are for date and offset
int dateOffset = decryptedValue.length - 5;
byte[] timePortion = new byte[dateOffset];
byte[] datePortion = new byte[3];
byte[] offsetPortion = new byte[2];
System.arraycopy(decryptedValue, 0, timePortion, 0, dateOffset);
System.arraycopy(decryptedValue, dateOffset, datePortion, 0, 3);
System.arraycopy(decryptedValue, dateOffset + 3, offsetPortion, 0, 2);
long localNanosSinceMidnight = readNanosSinceMidnightAE(timePortion, baseTypeInfo.getScale(),
baseSSType);
int daysIntoCE = getDaysIntoCE(datePortion, baseSSType);
int localMinutesOffset = ByteBuffer.wrap(offsetPortion).order(ByteOrder.LITTLE_ENDIAN).getShort();
return DDC.convertTemporalToObject(jdbcType, SSType.DATETIMEOFFSET,
new GregorianCalendar(new SimpleTimeZone(localMinutesOffset * 60 * 1000, ""), Locale.US),
daysIntoCE, localNanosSinceMidnight, baseTypeInfo.getScale());
}
case GUID: {
return Util.readGUID(decryptedValue);
}
default:
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_UnsupportedDataTypeAE"));
throw new SQLServerException(form.format(new Object[] {baseSSType}), null, 0, null);
}
}