diff --git a/source/libs/decimal/src/decimal.c b/source/libs/decimal/src/decimal.c index 2e5af032a5..2608be30e0 100644 --- a/source/libs/decimal/src/decimal.c +++ b/source/libs/decimal/src/decimal.c @@ -52,20 +52,28 @@ static uint8_t maxPrecision(DecimalInternalType type) { int32_t decimalGetRetType(const SDataType* pLeftT, const SDataType* pRightT, EOperatorType opType, SDataType* pOutType) { - if (IS_FLOAT_TYPE(pLeftT->type) || IS_FLOAT_TYPE(pRightT->type)) { + if (IS_FLOAT_TYPE(pLeftT->type) || IS_FLOAT_TYPE(pRightT->type) || IS_VAR_DATA_TYPE(pLeftT->type) || + IS_VAR_DATA_TYPE(pRightT->type)) { pOutType->type = TSDB_DATA_TYPE_DOUBLE; pOutType->bytes = tDataTypes[TSDB_DATA_TYPE_DOUBLE].bytes; return 0; } + if (IS_NULL_TYPE(pLeftT->type) || IS_NULL_TYPE(pRightT->type)) { + pOutType->type = TSDB_DATA_TYPE_NULL; + pOutType->bytes = tDataTypes[TSDB_DATA_TYPE_NULL].bytes; + return 0; + } + // TODO wjm check not supported types uint8_t p1 = pLeftT->precision, s1 = pLeftT->scale, p2 = pRightT->precision, s2 = pRightT->scale; - if (IS_DECIMAL_TYPE(pLeftT->type)) { - p2 = TSDB_DECIMAL128_MAX_PRECISION; + if (!IS_DECIMAL_TYPE(pLeftT->type)) { + p1 = TSDB_DECIMAL_MAX_PRECISION; s1 = s2; // TODO wjm take which scale? Maybe use default DecimalMax - } else { - p1 = TSDB_DECIMAL128_MAX_PRECISION; + } + if (!IS_DECIMAL_TYPE(pRightT->type)) { + p1 = TSDB_DECIMAL_MAX_PRECISION; s2 = s1; } @@ -90,8 +98,10 @@ int32_t decimalGetRetType(const SDataType* pLeftT, const SDataType* pRightT, EOp default: return TSDB_CODE_TSC_INVALID_OPERATION; } - pOutType->type = TSDB_DATA_TYPE_DECIMAL; - pOutType->bytes = tDataTypes[TSDB_DATA_TYPE_DECIMAL].bytes; + pOutType->precision = TMIN(pOutType->precision, TSDB_DECIMAL_MAX_PRECISION); + pOutType->scale = TMIN(pOutType->scale, TSDB_DECIMAL_MAX_SCALE); + pOutType->type = pOutType->precision > TSDB_DECIMAL64_MAX_PRECISION ? TSDB_DATA_TYPE_DECIMAL : TSDB_DATA_TYPE_DECIMAL64; + pOutType->bytes = tDataTypes[pOutType->type].bytes; return 0; } @@ -458,6 +468,7 @@ static Decimal128 SCALE_MULTIPLIER_128[38 + 1] = { #define DECIMAL128_ONE SCALE_MULTIPLIER_128[0] #define DECIMAL128_TEN SCALE_MULTIPLIER_128[1] +// TODO wjm pre define it?? actually, its MAX_INTEGER, not MAX #define DECIMAL128_GET_MAX(precision, pMax) \ do { \ *(pMax) = SCALE_MULTIPLIER_128[precision]; \ @@ -641,7 +652,6 @@ static int32_t decimal128ToStr(const DecimalType* pInt, uint8_t scale, char* pBu return 0; } for (int32_t i = digitNum - 1; i >= 0; --i) { - // TODO wjm test 0.0000000000000000000000000000000001 len += snprintf(buf + len, 64 - len, i == digitNum - 1 ? "%" PRIu64 : "%018" PRIu64, segments[i]); } int32_t wholeLen = len - scale; @@ -673,15 +683,18 @@ int32_t decimalToStr(const DecimalType* pDec, int8_t dataType, int8_t precision, int32_t decimalOp(EOperatorType op, const SDataType* pLeftT, const SDataType* pRightT, const SDataType* pOutT, const void* pLeftData, const void* pRightData, void* pOutputData) { int32_t code = 0; - if (pOutT->type != TSDB_DATA_TYPE_DECIMAL) return TSDB_CODE_INTERNAL_ERROR; + // TODO wjm if output precision <= 18, no need to convert to decimal128 - Decimal pLeft = *(Decimal*)pLeftData, pRight = *(Decimal*)pRightData; + Decimal pLeft = {0}, pRight = {0}; + SDataType tmpType = *pOutT; + tmpType.type = TSDB_DATA_TYPE_DECIMAL; + tmpType.precision = TSDB_DECIMAL_MAX_PRECISION; if (TSDB_DATA_TYPE_DECIMAL != pLeftT->type || pLeftT->scale != pOutT->scale) { - code = convertToDecimal(pLeftData, pLeftT, &pLeft, pOutT); + code = convertToDecimal(pLeftData, pLeftT, &pLeft, &tmpType); if (TSDB_CODE_SUCCESS != code) return code; } if (pRightT && (TSDB_DATA_TYPE_DECIMAL != pRightT->type || pRightT->scale != pOutT->scale)) { - code = convertToDecimal(pRightData, pRightT, &pRight, pOutT); + code = convertToDecimal(pRightData, pRightT, &pRight, &tmpType); if (TSDB_CODE_SUCCESS != code) return code; } @@ -693,7 +706,7 @@ int32_t decimalOp(EOperatorType op, const SDataType* pLeftT, const SDataType* pR default: break; } - return code; + return convertToDecimal(&pLeft, &tmpType, pOutputData, pOutT); } #define ABS_INT64(v) (v) == INT64_MIN ? (uint64_t)INT64_MAX + 1 : (uint64_t)llabs(v) @@ -796,8 +809,12 @@ static int32_t decimal128FromDecimal64(DecimalType* pDec, uint8_t prec, uint8_t decimal128ScaleTo(pDec, valScale, scale); return 0; } + static int32_t decimal128FromDecimal128(DecimalType* pDec, uint8_t prec, uint8_t scale, const DecimalType* pVal, uint8_t valPrec, uint8_t valScale) { + Decimal128 max = {0}; + DECIMAL128_GET_MAX(prec - scale, &max); + return 0; } #define CHECK_OVERFLOW_AND_MAKE_DECIMAL128(pDec, v, max, ABS) \ diff --git a/source/libs/decimal/test/decimalTest.cpp b/source/libs/decimal/test/decimalTest.cpp index f3e3ef67d0..11888707b7 100644 --- a/source/libs/decimal/test/decimalTest.cpp +++ b/source/libs/decimal/test/decimalTest.cpp @@ -230,6 +230,12 @@ TEST(decimal, decimalFromStr) { resExpect += 1; resExpect *= 100; ASSERT_EQ(res, resExpect); + + char buf[64] = "999.999"; + Decimal64 dec64 = {0}; + code = decimal64FromStr(buf, strlen(buf), 6, 3, &dec64); + ASSERT_EQ(code, 0); + ASSERT_EQ(999999, DECIMAL64_GET_VALUE(&dec64)); } TEST(decimal, toStr) { @@ -245,6 +251,78 @@ TEST(decimal, toStr) { ASSERT_STREQ(buf, "0"); } +SDataType getDecimalType(uint8_t prec, uint8_t scale) { + if (prec <= 18) { + return {.type = TSDB_DATA_TYPE_DECIMAL64, .precision = prec, .scale = scale, .bytes = 8}; + } else if (prec <= 38) { + return {.type = TSDB_DATA_TYPE_DECIMAL, .precision = prec, .scale = scale, .bytes = 16}; + } + return {}; +} + +bool operator==(const SDataType& lt, const SDataType& rt) { + return lt.type == rt.type && lt.precision == rt.precision && lt.scale == rt.scale && lt.bytes == rt.bytes; +} + +TEST(decimal, decimalOpRetType) { + EOperatorType op = OP_TYPE_ADD; + auto ta = getDecimalType(10, 2); + auto tb = getDecimalType(10, 2); + SDataType tc{}, tExpect = {.type = TSDB_DATA_TYPE_DECIMAL64, .precision = 11, .scale = 2, .bytes = sizeof(Decimal64)}; + int32_t code = decimalGetRetType(&ta, &tb, op, &tc); + ASSERT_EQ(code, 0); + ASSERT_EQ(tExpect, tc); + + ta.bytes = 8; + ta.type = TSDB_DATA_TYPE_TIMESTAMP; + + code = decimalGetRetType(&ta, &tb, op, &tc); + ASSERT_EQ(code, 0); + tExpect.type = TSDB_DATA_TYPE_DECIMAL; + tExpect.precision = TSDB_DECIMAL_MAX_PRECISION; + tExpect.scale = 2; + tExpect.bytes = sizeof(Decimal); + ASSERT_EQ(tExpect, tc); + + ta.bytes = 8; + ta.type = TSDB_DATA_TYPE_DOUBLE; + tc = {0}; + code = decimalGetRetType(&ta, &tb, op, &tc); + ASSERT_EQ(code, 0); + tExpect.type = TSDB_DATA_TYPE_DOUBLE; + tExpect.precision = 0; + tExpect.scale = 0; + tExpect.bytes = 8; + ASSERT_EQ(tExpect, tc); +} + +TEST(decimal, op) { + const char* stra = "123.99", * strb = "456.12"; + EOperatorType op = OP_TYPE_ADD; + auto ta = getDecimalType(10, 2); + Decimal64 a = {0}; + int32_t code = decimal64FromStr(stra, strlen(stra), ta.precision, ta.scale, &a); + ASSERT_EQ(code, 0); + + auto tb = getDecimalType(10, 2); + Decimal64 b{0}; + code = decimal64FromStr(strb, strlen(strb), tb.precision, tb.scale, &b); + ASSERT_EQ(code, 0); + + SDataType tc{}, tExpect{.type = TSDB_DATA_TYPE_DECIMAL64, .precision = 11, .scale = 2, .bytes = sizeof(Decimal64)}; + code = decimalGetRetType(&ta, &tb, op, &tc); + ASSERT_EQ(code, 0); + ASSERT_EQ(tc, tExpect); + Decimal64 res{}; + code = decimalOp(op, &ta, &tb, &tc, &a, &b, &res); + ASSERT_EQ(code, 0); + + char buf[64] = {0}; + code = decimalToStr(&res, TSDB_DATA_TYPE_DECIMAL64, tc.precision, tc.scale, buf, 64); + ASSERT_EQ(code, 0); + ASSERT_STREQ(buf, "580.11"); +} + int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();