in sdk/cosmos/azure-cosmos-encryption/src/main/java/com/azure/cosmos/encryption/implementation/mdesrc/cryptography/SqlSerializerUtil.java [342:566]
public static Object denormalizedValue(byte[] decryptedValue, JDBCType jdbcType, SSType ssType, int precision,
int scale, Calendar cal) throws MicrosoftDataEncryptionException {
Charset charset = null;
switch (ssType) {
case CHAR:
case VARCHAR:
case NCHAR:
case NVARCHAR:
case VARCHARMAX:
case NVARCHARMAX: {
try {
switch (ssType) {
case CHAR:
case VARCHAR:
case VARCHARMAX:
charset = UTF_8;
break;
case NCHAR:
case NVARCHAR:
case NVARCHARMAX:
charset = UTF_16LE;
break;
default:
// impossible to come here
MessageFormat form = new MessageFormat(
MicrosoftDataEncryptionExceptionResource.getResource("R_InvalidDataType"));
Object[] msgArgs = {ssType};
throw new MicrosoftDataEncryptionException(form.format(msgArgs));
}
String strVal = new String(decryptedValue, 0, decryptedValue.length, charset);
if ((SSType.CHAR == ssType) || (SSType.NCHAR == ssType)) {
// Right pad the string for CHAR types.
StringBuilder sb = new StringBuilder(strVal);
int padLength = precision - strVal.length();
for (int i = 0; i < padLength; i++) {
sb.append(' ');
}
strVal = sb.toString();
}
return convertStringToObject(strVal, jdbcType);
} catch (IllegalArgumentException e) {
MessageFormat form = new MessageFormat(
MicrosoftDataEncryptionExceptionResource.getResource("R_InvalidDataType"));
Object[] msgArgs = {ssType};
throw new MicrosoftDataEncryptionException(form.format(msgArgs));
} catch (UnsupportedEncodingException e) {
MessageFormat form = new MessageFormat(
MicrosoftDataEncryptionExceptionResource.getResource("R_InvalidEncoding"));
Object[] msgArgs = {charset};
throw new MicrosoftDataEncryptionException(form.format(msgArgs));
}
}
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(
MicrosoftDataEncryptionExceptionResource.getResource("R_InvalidDataType"));
Object[] msgArgs = {ssType};
throw new MicrosoftDataEncryptionException(form.format(msgArgs));
}
return convertLongToObject(readLong(decryptedValue, 0), jdbcType, ssType);
}
case REAL:
case FLOAT: {
// JDBC driver does not normalize real to float.
if (8 == decryptedValue.length) {
return convertDoubleToObject(
ByteBuffer.wrap(decryptedValue).order(ByteOrder.LITTLE_ENDIAN).getDouble(),
JDBCType.VARBINARY == jdbcType ? ssType.getJDBCType() : jdbcType);
} else if (4 == decryptedValue.length) {
return convertFloatToObject(
ByteBuffer.wrap(decryptedValue).order(ByteOrder.LITTLE_ENDIAN).getFloat(),
JDBCType.VARBINARY == jdbcType ? ssType.getJDBCType() : jdbcType);
} else {
MessageFormat form = new MessageFormat(
MicrosoftDataEncryptionExceptionResource.getResource("R_InvalidDataType"));
Object[] msgArgs = {ssType};
throw new MicrosoftDataEncryptionException(form.format(msgArgs));
}
}
case SMALLMONEY: {
return convertMoneyToObject(new BigDecimal(BigInteger.valueOf(readInt(decryptedValue, 4)), 4),
JDBCType.VARBINARY == jdbcType ? ssType.getJDBCType() : jdbcType, 4);
}
case MONEY: {
BigInteger bi = BigInteger.valueOf(
((long) readInt(decryptedValue, 0) << 32) | (readInt(decryptedValue, 4) & 0xFFFFFFFFL));
return convertMoneyToObject(new BigDecimal(bi, 4),
JDBCType.VARBINARY == jdbcType ? ssType.getJDBCType() : jdbcType, 8);
}
case NUMERIC:
case DECIMAL: {
return convertBigDecimalToObject(readBigDecimal(decryptedValue, decryptedValue.length, scale),
JDBCType.VARBINARY == jdbcType ? ssType.getJDBCType() : jdbcType);
}
case BINARY:
case VARBINARY:
case VARBINARYMAX: {
return convertBytesToObject(decryptedValue, jdbcType, ssType, precision);
}
case DATE: {
// get the number of days !! Size should be 3
if (3 != decryptedValue.length) {
MessageFormat form = new MessageFormat(
MicrosoftDataEncryptionExceptionResource.getResource("R_InvalidDataType"));
Object[] msgArgs = {ssType};
throw new MicrosoftDataEncryptionException(form.format(msgArgs));
}
// Getting number of days
// Following Lines of code copied from IOBuffer.readDaysIntoCE as we
// cannot reuse method
int daysIntoCE = getDaysIntoCE(decryptedValue, ssType);
return convertTemporalToObject(jdbcType, ssType, cal, daysIntoCE, 0, 0);
}
case TIME: {
long localNanosSinceMidnight = readNanosSinceMidnightAE(decryptedValue, scale, ssType);
return convertTemporalToObject(jdbcType, SSType.TIME, cal, 0, localNanosSinceMidnight, scale);
}
case DATETIME2: {
if (8 != decryptedValue.length) {
MessageFormat form = new MessageFormat(
MicrosoftDataEncryptionExceptionResource.getResource("R_InvalidDataType"));
Object[] msgArgs = {ssType};
throw new MicrosoftDataEncryptionException(form.format(msgArgs));
}
// 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, scale, ssType);
int daysIntoCE = getDaysIntoCE(datePortion, ssType);
// Convert the DATETIME2 value to the desired Java type.
return convertTemporalToObject(jdbcType, SSType.DATETIME2, cal, daysIntoCE, localNanosSinceMidnight,
scale);
}
case SMALLDATETIME: {
if (4 != decryptedValue.length) {
MessageFormat form = new MessageFormat(
MicrosoftDataEncryptionExceptionResource.getResource("R_InvalidDataType"));
Object[] msgArgs = {ssType};
throw new MicrosoftDataEncryptionException(form.format(msgArgs));
}
// SQL smalldatetime has less precision. It stores 2 bytes
// for the days since SQL Base Date and 2 bytes for minutes
// after midnight.
return convertTemporalToObject(jdbcType, SSType.DATETIME, cal, readUnsignedShort(decryptedValue, 0),
readUnsignedShort(decryptedValue, 2) * 60L * 1000L, 0);
}
case DATETIME: {
long ticksSinceMidnight = ((long)readInt(decryptedValue, 4) * 10 + 1) / 3;
if (8 != decryptedValue.length || Integer.MAX_VALUE < ticksSinceMidnight || ticksSinceMidnight < 0) {
MessageFormat form = new MessageFormat(
MicrosoftDataEncryptionExceptionResource.getResource("R_InvalidDataType"));
Object[] msgArgs = {ssType};
throw new MicrosoftDataEncryptionException(form.format(msgArgs));
}
// 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 convertTemporalToObject(jdbcType, SSType.DATETIME, cal, 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, scale, ssType);
int daysIntoCE = getDaysIntoCE(datePortion, ssType);
int localMinutesOffset = ByteBuffer.wrap(offsetPortion).order(ByteOrder.LITTLE_ENDIAN).getShort();
return convertTemporalToObject(jdbcType, SSType.DATETIMEOFFSET,
new GregorianCalendar(new SimpleTimeZone(localMinutesOffset * 60 * 1000, ""), Locale.US),
daysIntoCE, localNanosSinceMidnight, scale);
}
case GUID: {
return readGUID(decryptedValue);
}
default:
MessageFormat form = new MessageFormat(
MicrosoftDataEncryptionExceptionResource.getResource("R_InvalidDataType"));
Object[] msgArgs = {ssType};
throw new MicrosoftDataEncryptionException(form.format(msgArgs));
}
}