NValue NValue::opDivideDecimals(const NValue lhs, const NValue rhs) const { if ((lhs.getValueType() != VALUE_TYPE_DECIMAL) || (rhs.getValueType() != VALUE_TYPE_DECIMAL)) { throw SQLException(SQLException::dynamic_sql_error, "No decimal NValue in decimal subtract"); } if (lhs.isNull() || rhs.isNull()) { TTInt retval; retval.SetMin(); return getDecimalValue( retval ); } TTLInt calc; calc.FromInt(lhs.getDecimal()); calc *= NValue::kMaxScaleFactor; if (calc.Div(rhs.getDecimal())) { char message[4096]; snprintf( message, 4096, "Attempted to divide %s by %s causing overflow/underflow (or divide by zero)", lhs.createStringFromDecimal().c_str(), rhs.createStringFromDecimal().c_str()); throw SQLException(SQLException::data_exception_numeric_value_out_of_range, message); } TTInt retval; if (retval.FromInt(calc) || retval > NValue::s_maxDecimal || retval < s_minDecimal) { char message[4096]; snprintf( message, 4096, "Attempted to divide %s by %s causing overflow. Unscaled result was %s", lhs.createStringFromDecimal().c_str(), rhs.createStringFromDecimal().c_str(), calc.ToString(10).c_str()); throw SQLException(SQLException::data_exception_numeric_value_out_of_range, message); } return getDecimalValue(retval); }
/* * Avoid scaling both sides if possible. E.g, don't turn dec * 2 into * (dec * 2*kMaxScale*E-12). Then the result of simple multiplication * is a*b*E-24 and have to further multiply to get back to the assumed * E-12, which can overflow unnecessarily at the middle step. */ NValue NValue::opMultiplyDecimals(const NValue &lhs, const NValue &rhs) const { if ((lhs.getValueType() != VALUE_TYPE_DECIMAL) && (rhs.getValueType() != VALUE_TYPE_DECIMAL)) { throw SQLException(SQLException::dynamic_sql_error, "Non-decimal NValue in decimal multiply"); } if (lhs.isNull() || rhs.isNull()) { TTInt retval; retval.SetMin(); return getDecimalValue( retval ); } if ((lhs.getValueType() == VALUE_TYPE_DECIMAL) && (rhs.getValueType() == VALUE_TYPE_DECIMAL)) { TTLInt calc; calc.FromInt(lhs.getDecimal()); calc *= rhs.getDecimal(); calc /= NValue::kMaxScaleFactor; TTInt retval; if (retval.FromInt(calc) || retval > NValue::s_maxDecimal || retval < s_minDecimal) { char message[4096]; snprintf(message, 4096, "Attempted to multiply %s by %s causing overflow/underflow. Unscaled result was %s", lhs.createStringFromDecimal().c_str(), rhs.createStringFromDecimal().c_str(), calc.ToString(10).c_str()); throw SQLException(SQLException::data_exception_numeric_value_out_of_range, message); } return getDecimalValue(retval); } else if (lhs.getValueType() != VALUE_TYPE_DECIMAL) { TTLInt calc; calc.FromInt(rhs.getDecimal()); calc *= lhs.castAsDecimalAndGetValue(); calc /= NValue::kMaxScaleFactor; TTInt retval; retval.FromInt(calc); if (retval.FromInt(calc) || retval > NValue::s_maxDecimal || retval < s_minDecimal) { char message[4096]; snprintf(message, 4096, "Attempted to multiply %s by %s causing overflow/underflow. Unscaled result was %s", lhs.createStringFromDecimal().c_str(), rhs.createStringFromDecimal().c_str(), calc.ToString(10).c_str()); throw SQLException(SQLException::data_exception_numeric_value_out_of_range, message); } return getDecimalValue(retval); } else { TTLInt calc; calc.FromInt(lhs.getDecimal()); calc *= rhs.castAsDecimalAndGetValue(); calc /= NValue::kMaxScaleFactor; TTInt retval; retval.FromInt(calc); if (retval.FromInt(calc) || retval > NValue::s_maxDecimal || retval < s_minDecimal) { char message[4096]; snprintf(message, 4096, "Attempted to multiply %s by %s causing overflow/underflow. Unscaled result was %s", lhs.createStringFromDecimal().c_str(), rhs.createStringFromDecimal().c_str(), calc.ToString(10).c_str()); throw SQLException(SQLException::data_exception_numeric_value_out_of_range, message); } return getDecimalValue(retval); } }