Exemple #1
0
namespace voltdb {

Pool* NValue::getTempStringPool() {
    return ExecutorContext::getTempStringPool();
}


// For x<op>y where x is an integer,
// promote x and y to s_intPromotionTable[y]
ValueType NValue::s_intPromotionTable[] = {
    VALUE_TYPE_INVALID,   // 0 invalid
    VALUE_TYPE_NULL,      // 1 null
    VALUE_TYPE_INVALID,   // 2 <unused>
    VALUE_TYPE_BIGINT,    // 3 tinyint
    VALUE_TYPE_BIGINT,    // 4 smallint
    VALUE_TYPE_BIGINT,    // 5 integer
    VALUE_TYPE_BIGINT,    // 6 bigint
    VALUE_TYPE_INVALID,   // 7 <unused>
    VALUE_TYPE_DOUBLE,    // 8 double
    VALUE_TYPE_INVALID,   // 9 varchar
    VALUE_TYPE_INVALID,   // 10 <unused>
    VALUE_TYPE_BIGINT,    // 11 timestamp
    // 12 - 21 unused
    VALUE_TYPE_INVALID, VALUE_TYPE_INVALID, VALUE_TYPE_INVALID, VALUE_TYPE_INVALID,
    VALUE_TYPE_INVALID, VALUE_TYPE_INVALID, VALUE_TYPE_INVALID, VALUE_TYPE_INVALID,
    VALUE_TYPE_INVALID, VALUE_TYPE_INVALID,
    VALUE_TYPE_DECIMAL,   // 22 decimal
    VALUE_TYPE_INVALID,   // 23 boolean
    VALUE_TYPE_INVALID,   // 24 address
};

// for x<op>y where x is a double
// promote x and y to s_doublePromotionTable[y]
ValueType NValue::s_doublePromotionTable[] = {
    VALUE_TYPE_INVALID,   // 0 invalid
    VALUE_TYPE_NULL,      // 1 null
    VALUE_TYPE_INVALID,   // 2 <unused>
    VALUE_TYPE_DOUBLE,    // 3 tinyint
    VALUE_TYPE_DOUBLE,    // 4 smallint
    VALUE_TYPE_DOUBLE,    // 5 integer
    VALUE_TYPE_DOUBLE,    // 6 bigint
    VALUE_TYPE_INVALID,   // 7 <unused>
    VALUE_TYPE_DOUBLE,    // 8 double
    VALUE_TYPE_INVALID,   // 9 varchar
    VALUE_TYPE_INVALID,   // 10 <unused>
    VALUE_TYPE_DOUBLE,    // 11 timestamp
    // 12 - 21 unused.
    VALUE_TYPE_INVALID, VALUE_TYPE_INVALID, VALUE_TYPE_INVALID, VALUE_TYPE_INVALID,
    VALUE_TYPE_INVALID, VALUE_TYPE_INVALID, VALUE_TYPE_INVALID, VALUE_TYPE_INVALID,
    VALUE_TYPE_INVALID, VALUE_TYPE_INVALID,
    VALUE_TYPE_DOUBLE,    // 22 decimal
    VALUE_TYPE_INVALID,   // 23 boolean
    VALUE_TYPE_INVALID,   // 24 address
};

// for x<op>y where x is a decimal
// promote x and y to s_decimalPromotionTable[y]
ValueType NValue::s_decimalPromotionTable[] = {
    VALUE_TYPE_INVALID,   // 0 invalid
    VALUE_TYPE_NULL,      // 1 null
    VALUE_TYPE_INVALID,   // 2 <unused>
    VALUE_TYPE_DECIMAL,   // 3 tinyint
    VALUE_TYPE_DECIMAL,   // 4 smallint
    VALUE_TYPE_DECIMAL,   // 5 integer
    VALUE_TYPE_DECIMAL,   // 6 bigint
    VALUE_TYPE_INVALID,   // 7 <unused>
    VALUE_TYPE_DOUBLE,    // 8 double
    VALUE_TYPE_INVALID,   // 9 varchar
    VALUE_TYPE_INVALID,   // 10 <unused>
    VALUE_TYPE_DECIMAL,   // 11 timestamp
    // 12 - 21 unused. ick.
    VALUE_TYPE_INVALID, VALUE_TYPE_INVALID, VALUE_TYPE_INVALID, VALUE_TYPE_INVALID,
    VALUE_TYPE_INVALID, VALUE_TYPE_INVALID, VALUE_TYPE_INVALID, VALUE_TYPE_INVALID,
    VALUE_TYPE_INVALID, VALUE_TYPE_INVALID,
    VALUE_TYPE_DECIMAL,   // 22 decimal
    VALUE_TYPE_INVALID,   // 23 boolean
    VALUE_TYPE_INVALID,   // 24 address
};

TTInt NValue::s_maxDecimalValue("9999999999"   //10 digits
                                "9999999999"   //20 digits
                                "9999999999"   //30 digits
                                "99999999");    //38 digits

TTInt NValue::s_minDecimalValue("-9999999999"   //10 digits
                                 "9999999999"   //20 digits
                                 "9999999999"   //30 digits
                                 "99999999");    //38 digits

const double NValue::s_gtMaxDecimalAsDouble = 1E26;
const double NValue::s_ltMinDecimalAsDouble = -1E26;

TTInt NValue::s_maxInt64AsDecimal(TTInt(INT64_MAX) * kMaxScaleFactor);
TTInt NValue::s_minInt64AsDecimal(TTInt(-INT64_MAX) * kMaxScaleFactor);

/*
 * Produce a debugging string describing an NValue.
 */
std::string NValue::debug() const {
    const ValueType type = getValueType();
    if (isNull()) {
        return "<NULL>";
    }
    std::ostringstream buffer;
    std::string out_val;
    const char* ptr;
    int64_t addr;
    buffer << getTypeName(type) << "::";
    switch (type) {
    case VALUE_TYPE_BOOLEAN:
        buffer << (getBoolean() ? "true" : "false");
        break;
    case VALUE_TYPE_TINYINT:
        buffer << static_cast<int32_t>(getTinyInt());
        break;
    case VALUE_TYPE_SMALLINT:
        buffer << getSmallInt();
        break;
    case VALUE_TYPE_INTEGER:
        buffer << getInteger();
        break;
    case VALUE_TYPE_BIGINT:
    case VALUE_TYPE_TIMESTAMP:
        buffer << getBigInt();
        break;
    case VALUE_TYPE_DOUBLE:
        buffer << getDouble();
        break;
    case VALUE_TYPE_VARCHAR:
        ptr = reinterpret_cast<const char*>(getObjectValue_withoutNull());
        addr = reinterpret_cast<int64_t>(ptr);
        out_val = std::string(ptr, getObjectLength_withoutNull());
        buffer << "[" << getObjectLength_withoutNull() << "]";
        buffer << "\"" << out_val << "\"[@" << addr << "]";
        break;
    case VALUE_TYPE_VARBINARY:
        ptr = reinterpret_cast<const char*>(getObjectValue_withoutNull());
        addr = reinterpret_cast<int64_t>(ptr);
        out_val = std::string(ptr, getObjectLength_withoutNull());
        buffer << "[" << getObjectLength_withoutNull() << "]";
        buffer << "-bin[@" << addr << "]";
        break;
    case VALUE_TYPE_DECIMAL:
        buffer << createStringFromDecimal();
        break;
    default:
        buffer << "(no details)";
        break;
    }
    std::string ret(buffer.str());
    return (ret);
}


/**
 * Serialize sign and value using radix point (no exponent).
 */
std::string NValue::createStringFromDecimal() const {
    assert(!isNull());
    std::ostringstream buffer;
    TTInt scaledValue = getDecimal();
    if (scaledValue.IsSign()) {
        buffer << '-';
    }
    TTInt whole(scaledValue);
    TTInt fractional(scaledValue);
    whole /= NValue::kMaxScaleFactor;
    fractional %= NValue::kMaxScaleFactor;
    if (whole.IsSign()) {
        whole.ChangeSign();
    }
    buffer << whole.ToString(10);
    buffer << '.';
    if (fractional.IsSign()) {
        fractional.ChangeSign();
    }
    std::string fractionalString = fractional.ToString(10);
    for (int ii = static_cast<int>(fractionalString.size()); ii < NValue::kMaxDecScale; ii++) {
        buffer << '0';
    }
    buffer << fractionalString;
    return buffer.str();
}

/**
 *   Set a decimal value from a serialized representation
 *   This function does not handle scientific notation string, Java planner should convert that to plan string first.
 */
void NValue::createDecimalFromString(const std::string &txt) {
    if (txt.length() == 0) {
        throw SQLException(SQLException::volt_decimal_serialization_error,
                                       "Empty string provided");
    }
    bool setSign = false;
    if (txt[0] == '-') {
        setSign = true;
    }

    /**
     * Check for invalid characters
     */
    for (int ii = (setSign ? 1 : 0); ii < static_cast<int>(txt.size()); ii++) {
        if ((txt[ii] < '0' || txt[ii] > '9') && txt[ii] != '.') {
            char message[4096];
            snprintf(message, 4096, "Invalid characters in decimal string: %s",
                     txt.c_str());
            throw SQLException(SQLException::volt_decimal_serialization_error,
                               message);
        }
    }

    std::size_t separatorPos = txt.find( '.', 0);
    if (separatorPos == std::string::npos) {
        const std::string wholeString = txt.substr( setSign ? 1 : 0, txt.size());
        const std::size_t wholeStringSize = wholeString.size();
        if (wholeStringSize > 26) {
            throw SQLException(SQLException::volt_decimal_serialization_error,
                               "Maximum precision exceeded. Maximum of 26 digits to the left of the decimal point");
        }
        TTInt whole(wholeString);
        if (setSign) {
            whole.SetSign();
        }
        whole *= kMaxScaleFactor;
        getDecimal() = whole;
        return;
    }

    if (txt.find( '.', separatorPos + 1) != std::string::npos) {
        throw SQLException(SQLException::volt_decimal_serialization_error,
                           "Too many decimal points");
    }

    const std::string wholeString = txt.substr( setSign ? 1 : 0, separatorPos - (setSign ? 1 : 0));
    const std::size_t wholeStringSize = wholeString.size();
    if (wholeStringSize > 26) {
        throw SQLException(SQLException::volt_decimal_serialization_error,
                           "Maximum precision exceeded. Maximum of 26 digits to the left of the decimal point");
    }
    TTInt whole(wholeString);
    std::string fractionalString = txt.substr( separatorPos + 1, txt.size() - (separatorPos + 1));
    // remove trailing zeros
    while (fractionalString.size() > 0 && fractionalString[fractionalString.size() - 1] == '0')
        fractionalString.erase(fractionalString.size() - 1, 1);
    // check if too many decimal places
    if (fractionalString.size() > 12) {
        throw SQLException(SQLException::volt_decimal_serialization_error,
                           "Maximum scale exceeded. Maximum of 12 digits to the right of the decimal point");
    }
    while(fractionalString.size() < NValue::kMaxDecScale) {
        fractionalString.push_back('0');
    }
    TTInt fractional(fractionalString);

    whole *= kMaxScaleFactor;
    whole += fractional;

    if (setSign) {
        whole.SetSign();
    }

    getDecimal() = whole;
}

struct NValueList {
    static int allocationSizeForLength(size_t length)
    {
        //TODO: May want to consider extra allocation, here,
        // such as space for a sorted copy of the array.
        // This allocation has the advantage of getting freed via NValue::free.
        return (int)(sizeof(NValueList) + length*sizeof(StlFriendlyNValue));
    }

    void* operator new(size_t size, char* placement)
    {
        return placement;
    }
    void operator delete(void*, char*) {}
    void operator delete(void*) {}

    NValueList(size_t length, ValueType elementType) : m_length(length), m_elementType(elementType)
    { }

    void deserializeNValues(SerializeInputBE &input, Pool *dataPool)
    {
        for (int ii = 0; ii < m_length; ++ii) {
            m_values[ii].deserializeFromAllocateForStorage(m_elementType, input, dataPool);
        }
    }

    StlFriendlyNValue const* begin() const { return m_values; }
    StlFriendlyNValue const* end() const { return m_values + m_length; }

    const size_t m_length;
    const ValueType m_elementType;
    StlFriendlyNValue m_values[0];
};

/**
 * This NValue can be of any scalar value type.
 * @param rhs  a VALUE_TYPE_ARRAY NValue whose referent must be an NValueList.
 *             The NValue elements of the NValueList should be comparable to and ideally
 *             of exactly the same VALUE_TYPE as "this".
 * The planner and/or deserializer should have taken care of this with checks and
 * explicit cast operators and and/or constant promotions as needed.
 * @return a VALUE_TYPE_BOOLEAN NValue.
 */
bool NValue::inList(const NValue& rhs) const
{
    //TODO: research: does the SQL standard allow a null to match a null list element
    // vs. returning FALSE or NULL?
    const bool lhsIsNull = isNull();
    if (lhsIsNull) {
        return false;
    }

    const ValueType rhsType = rhs.getValueType();
    if (rhsType != VALUE_TYPE_ARRAY) {
        throwDynamicSQLException("rhs of IN expression is of a non-list type %s", rhs.getValueTypeString().c_str());
    }
    const NValueList* listOfNValues = (NValueList*)rhs.getObjectValue_withoutNull();
    const StlFriendlyNValue& value = *static_cast<const StlFriendlyNValue*>(this);
    //TODO: An O(ln(length)) implementation vs. the current O(length) implementation
    // such as binary search would likely require some kind of sorting/re-org of values
    // post-update/pre-lookup, and would likely require some sortable inequality method
    // (operator<()?) to be defined on StlFriendlyNValue.
    return std::find(listOfNValues->begin(), listOfNValues->end(), value) != listOfNValues->end();
}

void NValue::deserializeIntoANewNValueList(SerializeInputBE &input, Pool *dataPool)
{
    ValueType elementType = (ValueType)input.readByte();
    size_t length = input.readShort();
    int trueSize = NValueList::allocationSizeForLength(length);
    char* storage = allocateValueStorage(trueSize, dataPool);
    ::memset(storage, 0, trueSize);
    NValueList* nvset = new (storage) NValueList(length, elementType);
    nvset->deserializeNValues(input, dataPool);
    //TODO: An O(ln(length)) implementation vs. the current O(length) implementation of NValue::inList
    // would likely require some kind of sorting/re-org of values at this point post-update pre-lookup.
}

void NValue::allocateANewNValueList(size_t length, ValueType elementType)
{
    int trueSize = NValueList::allocationSizeForLength(length);
    char* storage = allocateValueStorage(trueSize, NULL);
    ::memset(storage, 0, trueSize);
    new (storage) NValueList(length, elementType);
}

void NValue::setArrayElements(std::vector<NValue> &args) const
{
    assert(m_valueType == VALUE_TYPE_ARRAY);
    NValueList* listOfNValues = (NValueList*)getObjectValue();
    // Assign each of the elements.
    int ii = (int)args.size();
    assert(ii == listOfNValues->m_length);
    while (ii--) {
        listOfNValues->m_values[ii] = args[ii];
    }
    //TODO: An O(ln(length)) implementation vs. the current O(length) implementation of NValue::inList
    // would likely require some kind of sorting/re-org of values at this point post-update pre-lookup.
}

int NValue::arrayLength() const
{
    assert(m_valueType == VALUE_TYPE_ARRAY);
    NValueList* listOfNValues = (NValueList*)getObjectValue();
    return static_cast<int>(listOfNValues->m_length);
}

NValue NValue::itemAtIndex(int index) const
{
    assert(m_valueType == VALUE_TYPE_ARRAY);
    NValueList* listOfNValues = (NValueList*)getObjectValue();
    assert(index >= 0);
    assert(index < listOfNValues->m_length);
    return listOfNValues->m_values[index];
}

void NValue::castAndSortAndDedupArrayForInList(const ValueType outputType, std::vector<NValue> &outList) const
{
    int size = arrayLength();

    // make a set to eliminate unique values in O(nlogn) time
    std::set<StlFriendlyNValue> uniques;

    // iterate over the array of values and build a sorted set of unique
    // values that don't overflow or violate unique constaints
    // (n.b. sorted set means dups are removed)
    for (int i = 0; i < size; i++) {
        NValue value = itemAtIndex(i);
        // cast the value to the right type and catch overflow/cast problems
        try {
            StlFriendlyNValue stlValue;
            stlValue = value.castAs(outputType);
            std::pair<std::set<StlFriendlyNValue>::iterator, bool> ret;
            ret = uniques.insert(stlValue);
        }
        // cast exceptions mean the in-list test is redundant
        // don't include these values in the materialized table
        // TODO: make this less hacky
        catch (SQLException &sqlException) {}
    }

    // insert all items in the set in order
    std::set<StlFriendlyNValue>::const_iterator iter;
    for (iter = uniques.begin(); iter != uniques.end(); iter++) {
        outList.push_back(*iter);
    }
}

void NValue::streamTimestamp(std::stringstream& value) const
{
    int64_t epoch_micros = getTimestamp();
    boost::gregorian::date as_date;
    boost::posix_time::time_duration as_time;
    micros_to_date_and_time(epoch_micros, as_date, as_time);

    long micro = epoch_micros % 1000000;
    if (epoch_micros < 0 && micro < 0) {
        // deal with negative micros (for dates before 1970)
        // by taking back the 1 whole second that was rounded down from the formatted date/time
        // and converting it to 1000000 micros
        micro += 1000000;
    }
    char mbstr[27];    // Format: "YYYY-MM-DD HH:MM:SS."- 20 characters + terminator
    snprintf(mbstr, sizeof(mbstr), "%04d-%02d-%02d %02d:%02d:%02d.%06d",
             (int)as_date.year(), (int)as_date.month(), (int)as_date.day(),
             (int)as_time.hours(), (int)as_time.minutes(), (int)as_time.seconds(), (int)micro);
    value << mbstr;
}

inline static void throwTimestampFormatError(const std::string &str)
{
    char message[4096];
    // No space separator for between the date and time
    snprintf(message, 4096, "Attempted to cast \'%s\' to type %s failed. Supported format: \'YYYY-MM-DD HH:MM:SS.UUUUUU\'"
             "or \'YYYY-MM-DD\'",
             str.c_str(), valueToString(VALUE_TYPE_TIMESTAMP).c_str());
    throw SQLException(SQLException::dynamic_sql_error, message);
}

int64_t NValue::parseTimestampString(const std::string &str)
{
    // date_str
    std::string date_str(str);
    // This is the std:string API for "ltrim" and "rtrim".
    date_str.erase(date_str.begin(), std::find_if(date_str.begin(), date_str.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
    date_str.erase(std::find_if(date_str.rbegin(), date_str.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), date_str.end());
    std::size_t sep_pos;


    int year = 0;
    int month = 0;
    int day = 0;
    int hour = 0;
    int minute = 0;
    int second = 0;
    int micro = 1000000;
    // time_str
    std::string time_str;
    std::string number_string;
    const char * pch;

    switch (date_str.size()) {
    case 26:
        sep_pos  = date_str.find(' ');
        if (sep_pos != 10) {
            throwTimestampFormatError(str);
        }

        time_str = date_str.substr(sep_pos + 1);
        // This is the std:string API for "ltrim"
        time_str.erase(time_str.begin(), std::find_if(time_str.begin(), time_str.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
        if (time_str.length() != 15) {
            throwTimestampFormatError(str);
        }

        // tokenize time_str: HH:MM:SS.mmmmmm
        if (time_str.at(2) != ':' || time_str.at(5) != ':' || time_str.at(8) != '.') {
            throwTimestampFormatError(str);
        }

        // HH
        number_string = time_str.substr(0,2);
        pch = number_string.c_str();
        if (pch[0] == '0') {
            hour = 0;
        } else if (pch[0] == '1') {
            hour = 10;
        } else if (pch[0] == '2') {
            hour = 20;
        } else {
            throwTimestampFormatError(str);
        }
        if (pch[1] > '9' || pch[1] < '0') {
            throwTimestampFormatError(str);
        }
        hour += pch[1] - '0';
        if (hour > 23 || hour < 0) {
            throwTimestampFormatError(str);
        }

        // MM
        number_string = time_str.substr(3,2);
        pch = number_string.c_str();
        if (pch[0] > '5' || pch[0] < '0') {
            throwTimestampFormatError(str);
        }
        minute = 10*(pch[0] - '0');
        if (pch[1] > '9' || pch[1] < '0') {
            throwTimestampFormatError(str);
        }
        minute += pch[1] - '0';
        if (minute > 59 || minute < 0) {
            throwTimestampFormatError(str);
        }

        // SS
        number_string = time_str.substr(6,2);
        pch = number_string.c_str();
        if (pch[0] > '5' || pch[0] < '0') {
            throwTimestampFormatError(str);
        }
        second = 10*(pch[0] - '0');
        if (pch[1] > '9' || pch[1] < '0') {
            throwTimestampFormatError(str);
        }
        second += pch[1] - '0';
        if (second > 59 || second < 0) {
            throwTimestampFormatError(str);
        }
        // hack a '1' in the place if the decimal and use atoi to get a value that
        // MUST be between 1 and 2 million if all 6 digits of micros were included.
        number_string = time_str.substr(8,7);
        number_string.at(0) = '1';
        pch = number_string.c_str();
        micro = atoi(pch);
        if (micro >= 2000000 || micro < 1000000) {
            throwTimestampFormatError(str);
        }
    case 10:
        if (date_str.at(4) != '-' || date_str.at(7) != '-') {
            throwTimestampFormatError(str);
        }

        number_string = date_str.substr(0,4);
        pch = number_string.c_str();

        // YYYY
        year = atoi(pch);
        // new years day 10000 is likely to cause problems.
        // There's a boost library limitation against years before 1400.
        if (year > 9999 || year < 1400) {
            throwTimestampFormatError(str);
        }

        // MM
        number_string = date_str.substr(5,2);
        pch = number_string.c_str();
        if (pch[0] == '0') {
            month = 0;
        } else if (pch[0] == '1') {
            month = 10;
        } else {
            throwTimestampFormatError(str);
        }
        if (pch[1] > '9' || pch[1] < '0') {
            throwTimestampFormatError(str);
        }
        month += pch[1] - '0';
        if (month > 12 || month < 1) {
            throwTimestampFormatError(str);
        }

        // DD
        number_string = date_str.substr(8,2);
        pch = number_string.c_str();
        if (pch[0] == '0') {
            day = 0;
        } else if (pch[0] == '1') {
            day = 10;
        } else if (pch[0] == '2') {
            day = 20;
        } else if (pch[0] == '3') {
            day = 30;
        } else {
            throwTimestampFormatError(str);
        }
        if (pch[1] > '9' || pch[1] < '0') {
            throwTimestampFormatError(str);
        }
        day += pch[1] - '0';
        if (day > 31 || day < 1) {
            throwTimestampFormatError(str);
        }
        break;
    default:
        throwTimestampFormatError(str);
    }

    int64_t result = 0;
    try {
        result = epoch_microseconds_from_components(
            (unsigned short int)year, (unsigned short int)month, (unsigned short int)day,
            hour, minute, second);
    } catch (const std::out_of_range& bad) {
        throwTimestampFormatError(str);
    }
    result += (micro - 1000000);
    return result;
}


int warn_if(int condition, const char* message)
{
    if (condition) {
        LogManager::getThreadLogger(LOGGERID_HOST)->log(LOGLEVEL_WARN, message);
    }
    return condition;
}

};
Exemple #2
0
/**
 *   Set a decimal value from a serialized representation
 *   This function does not handle scientific notation string, Java planner should convert that to plan string first.
 */
void NValue::createDecimalFromString(const std::string &txt) {
    if (txt.length() == 0) {
        throw SQLException(SQLException::volt_decimal_serialization_error,
                                       "Empty string provided");
    }
    bool setSign = false;
    if (txt[0] == '-') {
        setSign = true;
    }

    /**
     * Check for invalid characters
     */
    for (int ii = (setSign ? 1 : 0); ii < static_cast<int>(txt.size()); ii++) {
        if ((txt[ii] < '0' || txt[ii] > '9') && txt[ii] != '.') {
            char message[4096];
            snprintf(message, 4096, "Invalid characters in decimal string: %s",
                     txt.c_str());
            throw SQLException(SQLException::volt_decimal_serialization_error,
                               message);
        }
    }

    std::size_t separatorPos = txt.find( '.', 0);
    if (separatorPos == std::string::npos) {
        const std::string wholeString = txt.substr( setSign ? 1 : 0, txt.size());
        const std::size_t wholeStringSize = wholeString.size();
        if (wholeStringSize > 26) {
            throw SQLException(SQLException::volt_decimal_serialization_error,
                               "Maximum precision exceeded. Maximum of 26 digits to the left of the decimal point");
        }
        TTInt whole(wholeString);
        if (setSign) {
            whole.SetSign();
        }
        whole *= kMaxScaleFactor;
        getDecimal() = whole;
        return;
    }

    if (txt.find( '.', separatorPos + 1) != std::string::npos) {
        throw SQLException(SQLException::volt_decimal_serialization_error,
                           "Too many decimal points");
    }

    // This is set to 1 if we carry in the scale.
    int carryScale = 0;
    // This is set to 1 if we carry from the scale to the whole.
    int carryWhole = 0;

    // Start with the fractional part.  We need to
    // see if we need to carry from it first.
    std::string fractionalString = txt.substr( separatorPos + 1, txt.size() - (separatorPos + 1));
    // remove trailing zeros
    while (fractionalString.size() > 0 && fractionalString[fractionalString.size() - 1] == '0')
        fractionalString.erase(fractionalString.size() - 1, 1);
    //
    // If the scale is too large, then we will round
    // the number to the nearest 10**-12, and to the
    // furthest from zero if the number is equidistant
    // from the next highest and lowest.  This is the
    // definition of the Java rounding mode HALF_UP.
    //
    // At some point we will read a rounding mode from the
    // Java side at Engine configuration time, or something
    // like that, and have a whole flurry of rounding modes
    // here.  However, for now we have just the one.
    //
    if (fractionalString.size() > kMaxDecScale) {
        carryScale = ('5' <= fractionalString[kMaxDecScale]) ? 1 : 0;
        fractionalString = fractionalString.substr(0, kMaxDecScale);
    } else {
        while(fractionalString.size() < NValue::kMaxDecScale) {
            fractionalString.push_back('0');
        }
    }
    TTInt fractional(fractionalString);

    // If we decided to carry above, then do it here.
    // The fractional string is set up so that it represents
    // 1.0e-12 * units.
    fractional += carryScale;
    if (TTInt((uint64_t)kMaxScaleFactor) <= fractional) {
        // We know fractional was < kMaxScaleFactor before
        // we rounded, since fractional is 12 digits and
        // kMaxScaleFactor is 13.  So, if carrying makes
        // the fractional number too big, it must be eactly
        // too big.  That is to say, the rounded fractional
        // number number has become zero, and we need to
        // carry to the whole number.
        fractional = 0;
        carryWhole = 1;
    }

    // Process the whole number string.
    const std::string wholeString = txt.substr( setSign ? 1 : 0, separatorPos - (setSign ? 1 : 0));
    // We will check for oversize numbers below, so don't waste time
    // doing it now.
    TTInt whole(wholeString);
    whole += carryWhole;
    if (oversizeWholeDecimal(whole)) {
        throw SQLException(SQLException::volt_decimal_serialization_error,
                           "Maximum precision exceeded. Maximum of 26 digits to the left of the decimal point");
    }
    whole *= kMaxScaleFactor;
    whole += fractional;

    if (setSign) {
        whole.SetSign();
    }

    getDecimal() = whole;
}