in src/main/java/com/microsoft/sqlserver/jdbc/dtv.java [1445:1899]
final void executeOp(DTVExecuteOp op) throws SQLServerException {
JDBCType jdbcType = getJdbcType();
Object value = getSetterValue();
JavaType javaType = getJavaType();
boolean unsupportedConversion = false;
byte[] byteValue = null;
if (null != cryptoMeta && !SetterConversionAE.converts(javaType, jdbcType, sendStringParametersAsUnicode)) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unsupportedConversionAE"));
Object[] msgArgs = {javaType.toString().toLowerCase(Locale.ENGLISH),
jdbcType.toString().toLowerCase(Locale.ENGLISH)};
throw new SQLServerException(form.format(msgArgs), null);
}
if (null == value) {
switch (jdbcType) {
case NCHAR:
case NVARCHAR:
case LONGNVARCHAR:
case NCLOB:
if (null != cryptoMeta)
op.execute(this, (byte[]) null);
else
op.execute(this, (String) null);
break;
case INTEGER:
if (null != cryptoMeta)
op.execute(this, (byte[]) null);
else
op.execute(this, (Integer) null);
break;
case DATE:
op.execute(this, (java.sql.Date) null);
break;
case TIME:
op.execute(this, (java.sql.Time) null);
break;
case DATETIME:
case SMALLDATETIME:
case TIMESTAMP:
op.execute(this, (java.sql.Timestamp) null);
break;
case TIME_WITH_TIMEZONE:
case TIMESTAMP_WITH_TIMEZONE:
case DATETIMEOFFSET:
op.execute(this, (microsoft.sql.DateTimeOffset) null);
break;
case FLOAT:
case REAL:
if (null != cryptoMeta)
op.execute(this, (byte[]) null);
else
op.execute(this, (Float) null);
break;
case NUMERIC:
case DECIMAL:
case MONEY:
case SMALLMONEY:
if (null != cryptoMeta)
op.execute(this, (byte[]) null);
else
op.execute(this, (BigDecimal) null);
break;
case BINARY:
case VARBINARY:
case LONGVARBINARY:
case BLOB:
case CHAR:
case VARCHAR:
case LONGVARCHAR:
case CLOB:
case GUID:
op.execute(this, (byte[]) null);
break;
case TINYINT:
if (null != cryptoMeta)
op.execute(this, (byte[]) null);
else
op.execute(this, (Byte) null);
break;
case BIGINT:
if (null != cryptoMeta)
op.execute(this, (byte[]) null);
else
op.execute(this, (Long) null);
break;
case DOUBLE:
if (null != cryptoMeta)
op.execute(this, (byte[]) null);
else
op.execute(this, (Double) null);
break;
case SMALLINT:
if (null != cryptoMeta)
op.execute(this, (byte[]) null);
else
op.execute(this, (Short) null);
break;
case BIT:
case BOOLEAN:
if (null != cryptoMeta)
op.execute(this, (byte[]) null);
else
op.execute(this, (Boolean) null);
break;
case SQLXML:
op.execute(this, (SQLServerSQLXML) null);
break;
case ARRAY:
case DATALINK:
case DISTINCT:
case JAVA_OBJECT:
case NULL:
case OTHER:
case REF:
case ROWID:
case STRUCT:
unsupportedConversion = true;
break;
case SQL_VARIANT:
op.execute(this, (SqlVariant) null);
break;
case UNKNOWN:
default:
assert false : "Unexpected JDBCType: " + jdbcType;
unsupportedConversion = true;
break;
}
} else // null != value
{
if (aeLogger.isLoggable(java.util.logging.Level.FINE) && (null != cryptoMeta)) {
aeLogger.fine("Encrypting java data type: " + javaType);
}
switch (javaType) {
case STRING:
if (JDBCType.GUID == jdbcType) {
if (null != cryptoMeta) {
if (value instanceof String) {
value = UUID.fromString((String) value);
}
byte[] bArray = Util.asGuidByteArray((UUID) value);
op.execute(this, bArray);
} else {
op.execute(this, String.valueOf(value));
}
} else if (JDBCType.SQL_VARIANT == jdbcType) {
op.execute(this, String.valueOf(value));
} else if (JDBCType.GEOMETRY == jdbcType) {
op.execute(this, ((Geometry) value).serialize());
} else if (JDBCType.GEOGRAPHY == jdbcType) {
op.execute(this, ((Geography) value).serialize());
} else {
if (null != cryptoMeta) {
// if streaming types check for allowed data length in AE
// jdbcType is set to LONGNVARCHAR if input data length is >
// DataTypes.SHORT_VARTYPE_MAX_CHARS for string
if ((jdbcType == JDBCType.LONGNVARCHAR) && (JDBCType.VARCHAR == jdbcTypeSetByUser)
&& (DataTypes.MAX_VARTYPE_MAX_BYTES < valueLength)) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_StreamingDataTypeAE"));
Object[] msgArgs = {DataTypes.MAX_VARTYPE_MAX_BYTES, JDBCType.LONGVARCHAR};
throw new SQLServerException(this, form.format(msgArgs), null, 0, false);
} else if ((JDBCType.NVARCHAR == jdbcTypeSetByUser)
&& (DataTypes.MAX_VARTYPE_MAX_CHARS < valueLength)) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_StreamingDataTypeAE"));
Object[] msgArgs = {DataTypes.MAX_VARTYPE_MAX_CHARS, JDBCType.LONGNVARCHAR};
throw new SQLServerException(this, form.format(msgArgs), null, 0, false);
}
// Each character is represented using 2 bytes in NVARCHAR
else if ((JDBCType.NVARCHAR == jdbcTypeSetByUser) || (JDBCType.NCHAR == jdbcTypeSetByUser)
|| (JDBCType.LONGNVARCHAR == jdbcTypeSetByUser)) {
byteValue = ((String) value).getBytes(UTF_16LE);
}
// Each character is represented using 1 bytes in VARCHAR
else if ((JDBCType.VARCHAR == jdbcTypeSetByUser) || (JDBCType.CHAR == jdbcTypeSetByUser)
|| (JDBCType.LONGVARCHAR == jdbcTypeSetByUser)) {
byteValue = ((String) value).getBytes();
}
op.execute(this, byteValue);
} else
op.execute(this, (String) value);
}
break;
case INTEGER:
if (null != cryptoMeta) {
byteValue = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN)
.putLong(((Integer) value).longValue()).array();
op.execute(this, byteValue);
} else
op.execute(this, (Integer) value);
break;
case DATE:
op.execute(this, (java.sql.Date) value);
break;
case TIME:
op.execute(this, (java.sql.Time) value);
break;
case TIMESTAMP:
op.execute(this, (java.sql.Timestamp) value);
break;
case TVP:
op.execute(this, (TVP) value);
break;
case UTILDATE:
op.execute(this, (java.util.Date) value);
break;
case CALENDAR:
op.execute(this, (java.util.Calendar) value);
break;
case LOCALDATE:
op.execute(this, (LocalDate) value);
break;
case LOCALTIME:
op.execute(this, (LocalTime) value);
break;
case LOCALDATETIME:
op.execute(this, (LocalDateTime) value);
break;
case OFFSETTIME:
op.execute(this, (OffsetTime) value);
break;
case OFFSETDATETIME:
op.execute(this, (OffsetDateTime) value);
break;
case DATETIMEOFFSET:
op.execute(this, (microsoft.sql.DateTimeOffset) value);
break;
case GEOMETRY:
op.execute(this, ((Geometry) value).serialize());
break;
case GEOGRAPHY:
op.execute(this, ((Geography) value).serialize());
break;
case FLOAT:
if (null != cryptoMeta) {
if (Float.isInfinite((Float) value)) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_valueOutOfRange"));
throw new SQLServerException(form.format(new Object[] {jdbcType}), null, 0, null);
}
byteValue = ByteBuffer.allocate((Float.SIZE / Byte.SIZE)).order(ByteOrder.LITTLE_ENDIAN)
.putFloat((Float) value).array();
op.execute(this, byteValue);
} else
op.execute(this, (Float) value);
break;
case BIGDECIMAL:
if (null != cryptoMeta) {
// For AE, we need to use the setMoney/setSmallMoney methods to send encrypted data
// to money types. Also we need to use the TDS MONEYN rule for these.
// For these methods, the Java type is still BigDecimal, but the JDBCType
// would be JDBCType.MONEY or JDBCType.SMALLMONEY.
if ((JDBCType.MONEY == jdbcType) || (JDBCType.SMALLMONEY == jdbcType)) {
// For TDS we need to send the money value multiplied by 10^4 - this gives us the
// money value as integer. 4 is the default and only scale available with money.
// smallmoney is noralized to money.
BigDecimal bdValue = (BigDecimal) value;
// Need to validate range in the client side as we are converting BigDecimal to integers.
Util.validateMoneyRange(bdValue, jdbcType);
// Get the total number of digits after the multiplication. Scale is hardcoded to 4. This is
// needed to get the proper
// rounding.
// BigDecimal calculates precision a bit differently. For 0.000001, the precision is 1, but
// scale is 6 which makes the
// digit count -ve.
int digitCount = Math.max((bdValue.precision() - bdValue.scale()), 0) + 4;
long moneyVal = ((BigDecimal) value)
.multiply(new BigDecimal(10000), new MathContext(digitCount, RoundingMode.HALF_UP))
.longValue();
ByteBuffer bbuf = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
bbuf.putInt((int) (moneyVal >> 32)).array();
bbuf.putInt((int) moneyVal).array();
op.execute(this, bbuf.array());
} else {
BigDecimal bigDecimalVal = (BigDecimal) value;
byte[] decimalToByte = DDC.convertBigDecimalToBytes(bigDecimalVal, bigDecimalVal.scale());
byteValue = new byte[16];
// removing the precision and scale information from the decimalToByte array
System.arraycopy(decimalToByte, 2, byteValue, 0, decimalToByte.length - 2);
this.setScale(bigDecimalVal.scale());
if (null != cryptoMeta.getBaseTypeInfo()) {
// if the precision of the column is smaller than the precision of the actual value,
// the driver throws exception
if (cryptoMeta.getBaseTypeInfo().getPrecision() < Util
.getValueLengthBaseOnJavaType(bigDecimalVal, javaType, null, null, jdbcType)) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_valueOutOfRange"));
Object[] msgArgs = {cryptoMeta.getBaseTypeInfo().getSSTypeName()};
throw new SQLServerException(form.format(msgArgs),
SQLState.NUMERIC_DATA_OUT_OF_RANGE, DriverError.NOT_SET, null);
}
} else {
// if the precision that user provides is smaller than the precision of the actual
// value,
// the driver assumes the precision that user provides is the correct precision, and
// throws
// exception
if (valueLength < Util.getValueLengthBaseOnJavaType(bigDecimalVal, javaType, null, null,
jdbcType)) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_valueOutOfRange"));
Object[] msgArgs = {SSType.DECIMAL};
throw new SQLServerException(form.format(msgArgs),
SQLState.NUMERIC_DATA_OUT_OF_RANGE, DriverError.NOT_SET, null);
}
}
op.execute(this, byteValue);
}
} else
op.execute(this, (BigDecimal) value);
break;
case BYTEARRAY:
if ((null != cryptoMeta) && (DataTypes.MAX_VARTYPE_MAX_BYTES < valueLength)) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_StreamingDataTypeAE"));
Object[] msgArgs = {DataTypes.MAX_VARTYPE_MAX_BYTES, JDBCType.BINARY};
throw new SQLServerException(this, form.format(msgArgs), null, 0, false);
} else
op.execute(this, (byte[]) value);
break;
case BYTE:
// for tinyint
if (null != cryptoMeta) {
byteValue = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN)
.putLong((byte) value & 0xFF).array();
op.execute(this, byteValue);
} else
op.execute(this, (Byte) value);
break;
case LONG:
if (null != cryptoMeta) {
byteValue = ByteBuffer.allocate((Long.SIZE / Byte.SIZE)).order(ByteOrder.LITTLE_ENDIAN)
.putLong((Long) value).array();
op.execute(this, byteValue);
} else
op.execute(this, (Long) value);
break;
case BIGINTEGER:
op.execute(this, (java.math.BigInteger) value);
break;
case DOUBLE:
if (null != cryptoMeta) {
if (Double.isInfinite((Double) value)) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_valueOutOfRange"));
throw new SQLServerException(form.format(new Object[] {jdbcType}), null, 0, null);
}
byteValue = ByteBuffer.allocate((Double.SIZE / Byte.SIZE)).order(ByteOrder.LITTLE_ENDIAN)
.putDouble((Double) value).array();
op.execute(this, byteValue);
} else
op.execute(this, (Double) value);
break;
case SHORT:
if (null != cryptoMeta) {
byteValue = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN)
.putLong((short) value).array();
op.execute(this, byteValue);
} else
op.execute(this, (Short) value);
break;
case BOOLEAN:
if (null != cryptoMeta) {
byteValue = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN)
.putLong((Boolean) value ? 1 : 0).array();
op.execute(this, byteValue);
} else
op.execute(this, (Boolean) value);
break;
case BLOB:
op.execute(this, (Blob) value);
break;
case CLOB:
case NCLOB:
op.execute(this, (Clob) value);
break;
case INPUTSTREAM:
op.execute(this, (InputStream) value);
break;
case READER:
op.execute(this, (Reader) value);
break;
case SQLXML:
op.execute(this, (SQLServerSQLXML) value);
break;
default:
assert false : "Unexpected JavaType: " + javaType;
unsupportedConversion = true;
break;
}
}
if (unsupportedConversion) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unsupportedConversionFromTo"));
Object[] msgArgs = {javaType, jdbcType};
throw new SQLServerException(form.format(msgArgs), SQLState.DATA_EXCEPTION_NOT_SPECIFIC,
DriverError.NOT_SET, null);
}
}