final void executeOp()

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);
        }
    }