All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.microsoft.sqlserver.jdbc.DDC Maven / Gradle / Ivy

The newest version!
/*
 * Microsoft JDBC Driver for SQL Server Copyright(c) Microsoft Corporation All rights reserved. This program is made
 * available under the terms of the MIT License. See the LICENSE file in the project root for more information.
 */

package com.microsoft.sqlserver.jdbc;

import static java.nio.charset.StandardCharsets.US_ASCII;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;


/**
 * Utility class for all Data Dependent Conversions (DDC).
 */

final class DDC {

    private DDC() {
        throw new UnsupportedOperationException(SQLServerException.getErrString("R_notSupported"));
    }

    /**
     * Convert an Integer object to desired target user type.
     * 
     * @param intValue
     *        the value to convert.
     * @param valueLength
     *        the value to convert.
     * @param jdbcType
     *        the jdbc type required.
     * @param streamType
     *        the type of stream required.
     * @return the required object.
     */
    static final Object convertIntegerToObject(int intValue, int valueLength, JDBCType jdbcType,
            StreamType streamType) {
        switch (jdbcType) {
            case INTEGER:
                return intValue;
            case SMALLINT: // 2.21 small and tinyint returned as short
            case TINYINT:
                return (short) intValue;
            case BIT:
            case BOOLEAN:
                return 0 != intValue;
            case BIGINT:
                return (long) intValue;
            case DECIMAL:
            case NUMERIC:
            case MONEY:
            case SMALLMONEY:
                return new BigDecimal(Integer.toString(intValue));
            case FLOAT:
            case DOUBLE:
                return (double) intValue;
            case REAL:
                return (float) intValue;
            case BINARY:
                return convertIntToBytes(intValue, valueLength);
            case SQL_VARIANT:
                // return short or bit if the underlying datatype of sql_variant is tinyint, smallint or bit
                // otherwise, return integer
                // Longer datatypes such as double and float are handled by convertLongToObject instead.
                if (valueLength == 1) {
                    return 0 != intValue;
                } else if (valueLength == 3 || valueLength == 4) {
                    return (short) intValue;
                } else {
                    return intValue;
                }
            default:
                return Integer.toString(intValue);
        }
    }

    /**
     * Convert a Long object to desired target user type.
     * 
     * @param longVal
     *        the value to convert.
     * @param jdbcType
     *        the jdbc type required.
     * @param baseSSType
     *        the base SQLServer type.
     * @param streamType
     *        the stream type.
     * @return the required object.
     */
    static final Object convertLongToObject(long longVal, JDBCType jdbcType, SSType baseSSType, StreamType streamType) {
        switch (jdbcType) {
            case BIGINT:
            case SQL_VARIANT:
                return longVal;
            case INTEGER:
                return (int) longVal;
            case SMALLINT: // small and tinyint returned as short
            case TINYINT:
                return (short) longVal;
            case BIT:
            case BOOLEAN:
                return 0 != longVal;
            case DECIMAL:
            case NUMERIC:
            case MONEY:
            case SMALLMONEY:
                return new BigDecimal(Long.toString(longVal));
            case FLOAT:
            case DOUBLE:
                return (double) longVal;
            case REAL:
                return (float) longVal;
            case BINARY:
                byte[] convertedBytes = convertLongToBytes(longVal);
                int bytesToReturnLength;
                byte[] bytesToReturn;

                switch (baseSSType) {
                    case BIT:
                    case TINYINT:
                        bytesToReturnLength = 1;
                        bytesToReturn = new byte[bytesToReturnLength];
                        System.arraycopy(convertedBytes, convertedBytes.length - bytesToReturnLength, bytesToReturn, 0,
                                bytesToReturnLength);
                        return bytesToReturn;
                    case SMALLINT:
                        bytesToReturnLength = 2;
                        bytesToReturn = new byte[bytesToReturnLength];
                        System.arraycopy(convertedBytes, convertedBytes.length - bytesToReturnLength, bytesToReturn, 0,
                                bytesToReturnLength);
                        return bytesToReturn;
                    case INTEGER:
                        bytesToReturnLength = 4;
                        bytesToReturn = new byte[bytesToReturnLength];
                        System.arraycopy(convertedBytes, convertedBytes.length - bytesToReturnLength, bytesToReturn, 0,
                                bytesToReturnLength);
                        return bytesToReturn;
                    case BIGINT:
                        bytesToReturnLength = 8;
                        bytesToReturn = new byte[bytesToReturnLength];
                        System.arraycopy(convertedBytes, convertedBytes.length - bytesToReturnLength, bytesToReturn, 0,
                                bytesToReturnLength);
                        return bytesToReturn;
                    default:
                        return convertedBytes;
                }

            case VARBINARY:
                switch (baseSSType) {
                    case BIGINT:
                        return longVal;
                    case INTEGER:
                        return (int) longVal;
                    case SMALLINT: // small and tinyint returned as short
                    case TINYINT:
                        return (short) longVal;
                    case BIT:
                        return 0 != longVal;
                    case DECIMAL:
                    case NUMERIC:
                    case MONEY:
                    case SMALLMONEY:
                        return new BigDecimal(Long.toString(longVal));
                    case FLOAT:
                        return (double) longVal;
                    case REAL:
                        return (float) longVal;
                    case BINARY:
                        return convertLongToBytes(longVal);
                    default:
                        return Long.toString(longVal);
                }
            default:
                return Long.toString(longVal);
        }
    }

    /**
     * Encodes an integer value to a byte array in big-endian order.
     * 
     * @param intValue
     *        the integer value to encode.
     * @param valueLength
     *        the number of bytes to encode.
     * @return the byte array containing the big-endian encoded value.
     */
    static final byte[] convertIntToBytes(int intValue, int valueLength) {
        byte[] bytes = new byte[valueLength];
        for (int i = valueLength; i-- > 0;) {
            bytes[i] = (byte) (intValue & 0xFF);
            intValue >>= 8;
        }
        return bytes;
    }

    /**
     * Convert a Float object to desired target user type.
     * 
     * @param floatVal
     *        the value to convert.
     * @param jdbcType
     *        the jdbc type required.
     * @param streamType
     *        the stream type.
     * @return the required object.
     */
    static final Object convertFloatToObject(float floatVal, JDBCType jdbcType, StreamType streamType) {
        switch (jdbcType) {
            case REAL:
            case SQL_VARIANT:
                return floatVal;
            case INTEGER:
                return (int) floatVal;
            case SMALLINT: // small and tinyint returned as short
            case TINYINT:
                return (short) floatVal;
            case BIT:
            case BOOLEAN:
                return 0 != Float.compare(0.0f, floatVal);
            case BIGINT:
                return (long) floatVal;
            case DECIMAL:
            case NUMERIC:
            case MONEY:
            case SMALLMONEY:
                return new BigDecimal(Float.toString(floatVal));
            case FLOAT:
            case DOUBLE:
                return (Float.valueOf(floatVal)).doubleValue();
            case BINARY:
                return convertIntToBytes(Float.floatToRawIntBits(floatVal), 4);
            default:
                return Float.toString(floatVal);
        }
    }

    /**
     * Encodes a long value to a byte array in big-endian order.
     * 
     * @param longValue
     *        the long value to encode.
     * @return the byte array containing the big-endian encoded value.
     */
    static final byte[] convertLongToBytes(long longValue) {
        byte[] bytes = new byte[8];
        for (int i = 8; i-- > 0;) {
            bytes[i] = (byte) (longValue & 0xFF);
            longValue >>= 8;
        }
        return bytes;
    }

    /**
     * Convert a Double object to desired target user type.
     * 
     * @param doubleVal
     *        the value to convert.
     * @param jdbcType
     *        the jdbc type required.
     * @param streamType
     *        the stream type.
     * @return the required object.
     */
    static final Object convertDoubleToObject(double doubleVal, JDBCType jdbcType, StreamType streamType) {
        switch (jdbcType) {
            case FLOAT:
            case DOUBLE:
            case SQL_VARIANT:
                return doubleVal;
            case REAL:
                return (Double.valueOf(doubleVal)).floatValue();
            case INTEGER:
                return (int) doubleVal;
            case SMALLINT: // small and tinyint returned as short
            case TINYINT:
                return (short) doubleVal;
            case BIT:
            case BOOLEAN:
                return 0 != Double.compare(0.0d, doubleVal);
            case BIGINT:
                return (long) doubleVal;
            case DECIMAL:
            case NUMERIC:
            case MONEY:
            case SMALLMONEY:
                return new BigDecimal(Double.toString(doubleVal));
            case BINARY:
                return convertLongToBytes(Double.doubleToRawLongBits(doubleVal));
            default:
                return Double.toString(doubleVal);
        }
    }

    static final byte[] convertBigDecimalToBytes(BigDecimal bigDecimalVal, int scale) {
        byte[] valueBytes;

        if (bigDecimalVal == null) {
            valueBytes = new byte[2];
            valueBytes[0] = (byte) scale;
            valueBytes[1] = 0; // data length
        } else {
            boolean isNegative = (bigDecimalVal.signum() < 0);

            // NOTE: Handle negative scale as a special case for JDK 1.5 and later VMs.
            if (bigDecimalVal.scale() < 0)
                bigDecimalVal = bigDecimalVal.setScale(0);

            BigInteger bi = bigDecimalVal.unscaledValue();

            if (isNegative)
                bi = bi.negate();

            byte[] unscaledBytes = bi.toByteArray();

            valueBytes = new byte[unscaledBytes.length + 3];
            int j = 0;
            valueBytes[j++] = (byte) bigDecimalVal.scale();
            valueBytes[j++] = (byte) (unscaledBytes.length + 1); // data length + sign
            valueBytes[j++] = (byte) (isNegative ? 0 : 1); // 1 = +ve, 0 = -ve
            for (int i = unscaledBytes.length - 1; i >= 0; i--)
                valueBytes[j++] = unscaledBytes[i];
        }

        return valueBytes;
    }

    static final byte[] convertMoneyToBytes(BigDecimal bigDecimalVal, int bLength) throws SQLServerException {
        byte[] valueBytes = new byte[bLength];

        BigInteger bi = bigDecimalVal.unscaledValue();

        if (bLength == 8) {
            // money
            Util.validateMoneyRange(bigDecimalVal, JDBCType.MONEY);
            byte[] longbArray = new byte[bLength];
            Util.writeLong(bi.longValue(), longbArray, 0);
            /*
             * TDS 2.2.5.5.1.4 Fixed-Point Numbers Money is represented as a 8 byte signed integer, with one 4-byte
             * integer that represents the more significant half, and one 4-byte integer that represents the less
             * significant half.
             */
            System.arraycopy(longbArray, 0, valueBytes, 4, 4);
            System.arraycopy(longbArray, 4, valueBytes, 0, 4);
        } else {
            // smallmoney
            Util.validateMoneyRange(bigDecimalVal, JDBCType.SMALLMONEY);
            Util.writeInt(bi.intValue(), valueBytes, 0);
        }

        return valueBytes;
    }

    /**
     * Convert a BigDecimal object to desired target user type.
     * 
     * @param bigDecimalVal
     *        the value to convert.
     * @param jdbcType
     *        the jdbc type required.
     * @param streamType
     *        the stream type.
     * @return the required object.
     */
    static final Object convertBigDecimalToObject(BigDecimal bigDecimalVal, JDBCType jdbcType, StreamType streamType) {
        switch (jdbcType) {
            case DECIMAL:
            case NUMERIC:
            case MONEY:
            case SMALLMONEY:
            case SQL_VARIANT:
                return bigDecimalVal;
            case FLOAT:
            case DOUBLE:
                return bigDecimalVal.doubleValue();
            case REAL:
                return bigDecimalVal.floatValue();
            case INTEGER:
                return bigDecimalVal.intValue();
            case SMALLINT: // small and tinyint returned as short
            case TINYINT:
                return bigDecimalVal.shortValue();
            case BIT:
            case BOOLEAN:
                return 0 != bigDecimalVal.compareTo(BigDecimal.valueOf(0));
            case BIGINT:
                return bigDecimalVal.longValue();
            case BINARY:
                return convertBigDecimalToBytes(bigDecimalVal, bigDecimalVal.scale());
            default:
                return bigDecimalVal.toString();
        }
    }

    /**
     * Convert a Money object to desired target user type.
     * 
     * @param bigDecimalVal
     *        the value to convert.
     * @param jdbcType
     *        the jdbc type required.
     * @param streamType
     *        the stream type.
     * @param numberOfBytes
     *        the number of bytes to convert
     * @return the required object.
     */
    static final Object convertMoneyToObject(BigDecimal bigDecimalVal, JDBCType jdbcType, StreamType streamType,
            int numberOfBytes) {
        switch (jdbcType) {
            case DECIMAL:
            case NUMERIC:
            case MONEY:
            case SMALLMONEY:
                return bigDecimalVal;
            case FLOAT:
            case DOUBLE:
                return bigDecimalVal.doubleValue();
            case REAL:
                return bigDecimalVal.floatValue();
            case INTEGER:
                return bigDecimalVal.intValue();
            case SMALLINT: // small and tinyint returned as short
            case TINYINT:
                return bigDecimalVal.shortValue();
            case BIT:
            case BOOLEAN:
                return 0 != bigDecimalVal.compareTo(BigDecimal.valueOf(0));
            case BIGINT:
                return bigDecimalVal.longValue();
            case BINARY:
                return convertToBytes(bigDecimalVal, bigDecimalVal.scale(), numberOfBytes);
            default:
                return bigDecimalVal.toString();
        }
    }

    // converts big decimal to money and smallmoney
    private static byte[] convertToBytes(BigDecimal value, int scale, int numBytes) {
        boolean isNeg = value.signum() < 0;

        value = value.setScale(scale);

        BigInteger bigInt = value.unscaledValue();

        byte[] unscaledBytes = bigInt.toByteArray();

        byte[] ret = new byte[numBytes];
        if (unscaledBytes.length < numBytes) {
            for (int i = 0; i < numBytes - unscaledBytes.length; ++i) {
                ret[i] = (byte) (isNeg ? -1 : 0);
            }
        }
        int offset = numBytes - unscaledBytes.length;
        System.arraycopy(unscaledBytes, 0, ret, offset, numBytes - offset);
        return ret;
    }

    /**
     * Convert a byte array to desired target user type.
     * 
     * @param bytesValue
     *        the value to convert.
     * @param jdbcType
     *        the jdbc type required.
     * @param baseTypeInfo
     *        the type information associated with bytesValue.
     * @return the required object.
     * @throws SQLServerException
     *         when an error occurs.
     */
    static final Object convertBytesToObject(byte[] bytesValue, JDBCType jdbcType,
            TypeInfo baseTypeInfo) throws SQLServerException {
        switch (jdbcType) {
            case CHAR:
                String str = Util.bytesToHexString(bytesValue, bytesValue.length);

                if ((SSType.BINARY == baseTypeInfo.getSSType()) && (str.length() < (baseTypeInfo.getPrecision() * 2))) {

                    StringBuilder strbuf = new StringBuilder(str);

                    while (strbuf.length() < (baseTypeInfo.getPrecision() * 2)) {
                        strbuf.append('0');
                    }
                    return strbuf.toString();
                }
                return str;

            case BINARY:
            case VARBINARY:
            case LONGVARBINARY:
                if ((SSType.BINARY == baseTypeInfo.getSSType()) && (bytesValue.length < baseTypeInfo.getPrecision())) {

                    byte[] newBytes = new byte[baseTypeInfo.getPrecision()];
                    System.arraycopy(bytesValue, 0, newBytes, 0, bytesValue.length);
                    return newBytes;
                }

                return bytesValue;

            default:
                MessageFormat form = new MessageFormat(
                        SQLServerException.getErrString("R_unsupportedConversionFromTo"));
                throw new SQLServerException(form.format(new Object[] {baseTypeInfo.getSSType().name(), jdbcType}),
                        null, 0, null);
        }
    }

    /**
     * Convert a String object to desired target user type.
     * 
     * @param stringVal
     *        the value to convert.
     * @param charset
     *        the character set.
     * @param jdbcType
     *        the jdbc type required.
     * @return the required object.
     */
    static final Object convertStringToObject(String stringVal, Charset charset, JDBCType jdbcType,
            StreamType streamType) throws UnsupportedEncodingException {
        switch (jdbcType) {
            // Convert String to Numeric types.
            case DECIMAL:
            case NUMERIC:
            case MONEY:
            case SMALLMONEY:
                return new BigDecimal(stringVal.trim());
            case FLOAT:
            case DOUBLE:
                return Double.valueOf(stringVal.trim());
            case REAL:
                return Float.valueOf(stringVal.trim());
            case INTEGER:
                return Integer.valueOf(stringVal.trim());
            case SMALLINT: // small and tinyint returned as short
            case TINYINT:
                return Short.valueOf(stringVal.trim());
            case BIT:
            case BOOLEAN:
                String trimmedString = stringVal.trim();
                return (1 == trimmedString.length()) ? Boolean.valueOf('1' == trimmedString.charAt(0))
                                                     : Boolean.valueOf(trimmedString);
            case BIGINT:
                return Long.valueOf(stringVal.trim());

            // Convert String to Temporal types.
            case TIMESTAMP:
                return java.sql.Timestamp.valueOf(stringVal.trim());
            case LOCALDATETIME:
                return parseStringIntoLDT(stringVal.trim());
            case DATE:
                return java.sql.Date.valueOf(getDatePart(stringVal.trim()));
            case TIME:
                // Accepted character formats for conversion to java.sql.Time are:
                // hh:mm:ss[.nnnnnnnnn]
                // YYYY-MM-DD hh:mm:ss[.nnnnnnnnn]
                //
                // To handle either of these formats:
                // 1) Normalize and parse as a Timestamp
                // 2) Round fractional seconds up to the nearest millisecond (max resolution of java.sql.Time)
                // 3) Renormalize (as rounding may have changed the date) to a java.sql.Time
                java.sql.Timestamp ts = java.sql.Timestamp
                        .valueOf(TDS.BASE_DATE_1970 + " " + getTimePart(stringVal.trim()));
                GregorianCalendar cal = new GregorianCalendar(Locale.US);
                cal.clear();
                cal.setTimeInMillis(ts.getTime());
                if (ts.getNanos() % Nanos.PER_MILLISECOND >= Nanos.PER_MILLISECOND / 2)
                    cal.add(Calendar.MILLISECOND, 1);
                cal.set(TDS.BASE_YEAR_1970, Calendar.JANUARY, 1);
                return new java.sql.Time(cal.getTimeInMillis());

            case BINARY:
                return stringVal.getBytes(charset);

            default:
                // For everything else, just return either a string or appropriate stream.
                switch (streamType) {
                    case CHARACTER:
                        return new StringReader(stringVal);
                    case ASCII:
                        return new ByteArrayInputStream(stringVal.getBytes(US_ASCII));
                    case BINARY:
                        return new ByteArrayInputStream(stringVal.getBytes());

                    default:
                        return stringVal;
                }
        }
    }

    /**
     * Taken from java.sql.Timestamp implementation
     * 
     * @param s
     *        String to be parsed
     * @return LocalDateTime
     */
    private static LocalDateTime parseStringIntoLDT(String s) {
        final int YEAR_LENGTH = 4;
        final int MONTH_LENGTH = 2;
        final int DAY_LENGTH = 2;
        final int MAX_MONTH = 12;
        final int MAX_DAY = 31;
        int year = 0;
        int month = 0;
        int day = 0;
        int hour;
        int minute;
        int second;
        int nanos = 0;
        int firstDash;
        int secondDash;
        int dividingSpace;
        int firstColon;
        int secondColon;
        int period;
        String formatError = "Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]";

        if (s == null)
            throw new java.lang.IllegalArgumentException("null string");

        // Split the string into date and time components
        s = s.trim();
        dividingSpace = s.indexOf(' ');
        if (dividingSpace < 0) {
            throw new java.lang.IllegalArgumentException(formatError);
        }

        // Parse the date
        firstDash = s.indexOf('-');
        secondDash = s.indexOf('-', firstDash + 1);

        // Parse the time
        firstColon = s.indexOf(':', dividingSpace + 1);
        secondColon = s.indexOf(':', firstColon + 1);
        period = s.indexOf('.', secondColon + 1);

        // Convert the date
        boolean parsedDate = false;
        if (firstDash > 0 && secondDash > 0 && secondDash < dividingSpace - 1 && firstDash == YEAR_LENGTH
                && (secondDash - firstDash > 1 && secondDash - firstDash <= MONTH_LENGTH + 1)
                && (dividingSpace - secondDash > 1 && dividingSpace - secondDash <= DAY_LENGTH + 1)) {
            year = Integer.parseInt(s.substring(0, firstDash));
            month = Integer.parseInt(s.substring(firstDash + 1, secondDash));
            day = Integer.parseInt(s.substring(secondDash + 1, dividingSpace));

            if ((month >= 1 && month <= MAX_MONTH) && (day >= 1 && day <= MAX_DAY)) {
                parsedDate = true;
            }
        }

        if (!parsedDate) {
            throw new java.lang.IllegalArgumentException(formatError);
        }

        // Convert the time; default missing nanos
        int len = s.length();
        if (firstColon > 0 && secondColon > 0 && secondColon < len - 1) {
            hour = Integer.parseInt(s.substring(dividingSpace + 1, firstColon));
            minute = Integer.parseInt(s.substring(firstColon + 1, secondColon));
            if (period > 0 && period < len - 1) {
                second = Integer.parseInt(s.substring(secondColon + 1, period));
                int nanoPrecision = len - (period + 1);
                if (nanoPrecision > 9)
                    throw new java.lang.IllegalArgumentException(formatError);
                if (!Character.isDigit(s.charAt(period + 1)))
                    throw new java.lang.IllegalArgumentException(formatError);
                int tmpNanos = Integer.parseInt(s.substring(period + 1, len));
                while (nanoPrecision < 9) {
                    tmpNanos *= 10;
                    nanoPrecision++;
                }
                nanos = tmpNanos;
            } else if (period > 0) {
                throw new java.lang.IllegalArgumentException(formatError);
            } else {
                second = Integer.parseInt(s.substring(secondColon + 1, len));
            }
        } else {
            throw new java.lang.IllegalArgumentException(formatError);
        }
        return LocalDateTime.of(year, month, day, hour, minute, second, nanos);
    }

    static final Object convertStreamToObject(BaseInputStream stream, TypeInfo typeInfo, JDBCType jdbcType,
            InputStreamGetterArgs getterArgs) throws SQLServerException {
        // Need to handle the simple case of a null value here, as it is not done
        // outside this function.
        if (null == stream)
            return null;

        assert null != typeInfo;
        assert null != getterArgs;

        SSType ssType = typeInfo.getSSType();

        try {
            switch (jdbcType) {
                case CLOB:
                    return new SQLServerClob(stream, typeInfo);
                case NCLOB:
                    return new SQLServerNClob(stream, typeInfo);
                case SQLXML:
                    return new SQLServerSQLXML(stream, getterArgs, typeInfo);
                case BINARY:
                case VARBINARY:
                case LONGVARBINARY:
                case BLOB:
                    // Where allowed, streams convert directly to binary representation
                    if (StreamType.BINARY == getterArgs.streamType)
                        return stream;
                    if (JDBCType.BLOB == jdbcType)
                        return new SQLServerBlob(stream);
                    return stream.getBytes();
                case CHAR:
                case VARCHAR:
                case LONGVARCHAR:
                case NCHAR:
                case NVARCHAR:
                case LONGNVARCHAR:
                default:
                    // Binary streams to character types:
                    // - Direct conversion to ASCII stream
                    // - Convert as hexized value to other character types
                    if (SSType.BINARY == ssType || SSType.VARBINARY == ssType || SSType.VARBINARYMAX == ssType
                            || SSType.TIMESTAMP == ssType || SSType.IMAGE == ssType || SSType.UDT == ssType) {
                        if (StreamType.ASCII == getterArgs.streamType) {
                            return stream;
                        } else {
                            assert StreamType.CHARACTER == getterArgs.streamType
                                    || StreamType.NONE == getterArgs.streamType;

                            byte[] byteValue = stream.getBytes();
                            if (JDBCType.GUID == jdbcType) {
                                return Util.readGUID(byteValue);
                            } else if (JDBCType.GEOMETRY == jdbcType) {
                                if (!typeInfo.getSSTypeName().equalsIgnoreCase(jdbcType.toString())) {
                                    DataTypes.throwConversionError(typeInfo.getSSTypeName().toUpperCase(),
                                            jdbcType.toString());
                                }
                                return Geometry.STGeomFromWKB(byteValue);
                            } else if (JDBCType.GEOGRAPHY == jdbcType) {
                                if (!typeInfo.getSSTypeName().equalsIgnoreCase(jdbcType.toString())) {
                                    DataTypes.throwConversionError(typeInfo.getSSTypeName().toUpperCase(),
                                            jdbcType.toString());
                                }
                                return Geography.STGeomFromWKB(byteValue);
                            } else {
                                String hexString = Util.bytesToHexString(byteValue, byteValue.length);

                                if (StreamType.NONE == getterArgs.streamType)
                                    return hexString;

                                return new StringReader(hexString);
                            }
                        }
                    }

                    // Handle streams converting to ASCII
                    if (StreamType.ASCII == getterArgs.streamType) {
                        // Fast path for SBCS data that converts directly/easily to ASCII
                        if (typeInfo.supportsFastAsciiConversion())
                            return new AsciiFilteredInputStream(stream);

                        // Slightly less fast path for MBCS data that converts directly/easily to ASCII
                        if (getterArgs.isAdaptive) {
                            return AsciiFilteredUnicodeInputStream.makeAsciiFilteredUnicodeInputStream(stream,
                                    new BufferedReader(new InputStreamReader(stream, typeInfo.getCharset())));
                        } else {
                            return new ByteArrayInputStream(
                                    (new String(stream.getBytes(), typeInfo.getCharset())).getBytes(US_ASCII));
                        }
                    } else if (StreamType.CHARACTER == getterArgs.streamType
                            || StreamType.NCHARACTER == getterArgs.streamType) {
                        if (getterArgs.isAdaptive)
                            return new BufferedReader(new InputStreamReader(stream, typeInfo.getCharset()));
                        else
                            return new StringReader(new String(stream.getBytes(), typeInfo.getCharset()));
                    }

                    // None of the special/fast textual conversion cases applied. Just go the normal route of converting
                    // via String.
                    return convertStringToObject(new String(stream.getBytes(), typeInfo.getCharset()),
                            typeInfo.getCharset(), jdbcType, getterArgs.streamType);

            }
        }

        // Conversion can throw either of these exceptions:
        //
        // UnsupportedEncodingException (binary conversions)
        // IllegalArgumentException (any conversion - note: numerics throw NumberFormatException subclass)
        //
        // Catch them and translate them to a SQLException so that we don't propagate an unexpected exception
        // type all the way up to the app, which may not catch it either...
        catch (IllegalArgumentException | UnsupportedEncodingException e) {
            MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_errorConvertingValue"));
            throw new SQLServerException(form.format(new Object[] {typeInfo.getSSType(), jdbcType}), null, 0, e);
        }
    }

    // Returns date portion of string.
    // Expects one of "" or "




© 2015 - 2024 Weber Informatics LLC | Privacy Policy