diff --git a/include/util/tunit.h b/include/util/tunit.h index de37c85929..207431fa7d 100644 --- a/include/util/tunit.h +++ b/include/util/tunit.h @@ -22,10 +22,10 @@ extern "C" { #endif -int64_t taosStrHumanToInt64(const char* str); +int32_t taosStrHumanToInt64(const char* str, int64_t* out); void taosInt64ToHumanStr(int64_t val, char* outStr); -int32_t taosStrHumanToInt32(const char* str); +int32_t taosStrHumanToInt32(const char* str, int32_t* out); void taosInt32ToHumanStr(int32_t val, char* outStr); #ifdef __cplusplus diff --git a/include/util/tutil.h b/include/util/tutil.h index de2cd205f2..54ce6fc849 100644 --- a/include/util/tutil.h +++ b/include/util/tutil.h @@ -56,6 +56,8 @@ void taosIpPort2String(uint32_t ip, uint16_t port, char *str); void *tmemmem(const char *haystack, int hlen, const char *needle, int nlen); +int32_t parseCfgReal(const char* str, double* out); + static FORCE_INLINE void taosEncryptPass(uint8_t *inBuf, size_t inLen, char *target) { T_MD5_CTX context; tMD5Init(&context); diff --git a/source/util/src/tconfig.c b/source/util/src/tconfig.c index 8fdc2654c5..caca123777 100644 --- a/source/util/src/tconfig.c +++ b/source/util/src/tconfig.c @@ -174,7 +174,9 @@ static int32_t cfgSetBool(SConfigItem *pItem, const char *value, ECfgSrcType sty } static int32_t cfgSetInt32(SConfigItem *pItem, const char *value, ECfgSrcType stype) { - int32_t ival = taosStrHumanToInt32(value); + int32_t ival; + int32_t code = taosStrHumanToInt32(value, &ival); + if (code != TSDB_CODE_SUCCESS) return code; if (ival < pItem->imin || ival > pItem->imax) { uError("cfg:%s, type:%s src:%s value:%d out of range[%" PRId64 ", %" PRId64 "]", pItem->name, cfgDtypeStr(pItem->dtype), cfgStypeStr(stype), ival, pItem->imin, pItem->imax); @@ -188,7 +190,9 @@ static int32_t cfgSetInt32(SConfigItem *pItem, const char *value, ECfgSrcType st } static int32_t cfgSetInt64(SConfigItem *pItem, const char *value, ECfgSrcType stype) { - int64_t ival = taosStrHumanToInt64(value); + int64_t ival; + int32_t code = taosStrHumanToInt64(value, &ival); + if (code != TSDB_CODE_SUCCESS) return code; if (ival < pItem->imin || ival > pItem->imax) { uError("cfg:%s, type:%s src:%s value:%" PRId64 " out of range[%" PRId64 ", %" PRId64 "]", pItem->name, cfgDtypeStr(pItem->dtype), cfgStypeStr(stype), ival, pItem->imin, pItem->imax); @@ -202,15 +206,16 @@ static int32_t cfgSetInt64(SConfigItem *pItem, const char *value, ECfgSrcType st } static int32_t cfgSetFloat(SConfigItem *pItem, const char *value, ECfgSrcType stype) { - float fval = (float)atof(value); - if (fval < pItem->fmin || fval > pItem->fmax) { + double dval; + int32_t code = parseCfgReal(value, &dval); + if (dval < pItem->fmin || dval > pItem->fmax) { uError("cfg:%s, type:%s src:%s value:%f out of range[%f, %f]", pItem->name, cfgDtypeStr(pItem->dtype), - cfgStypeStr(stype), fval, pItem->fmin, pItem->fmax); + cfgStypeStr(stype), dval, pItem->fmin, pItem->fmax); terrno = TSDB_CODE_OUT_OF_RANGE; return -1; } - pItem->fval = fval; + pItem->fval = (float)dval; pItem->stype = stype; return 0; } @@ -408,7 +413,9 @@ int32_t cfgCheckRangeForDynUpdate(SConfig *pCfg, const char *name, const char *p } } break; case CFG_DTYPE_INT32: { - int32_t ival = (int32_t)taosStrHumanToInt32(pVal); + int32_t ival; + int32_t code = (int32_t)taosStrHumanToInt32(pVal, &ival); + if (code != TSDB_CODE_SUCCESS) return code; if (ival < pItem->imin || ival > pItem->imax) { uError("cfg:%s, type:%s value:%d out of range[%" PRId64 ", %" PRId64 "]", pItem->name, cfgDtypeStr(pItem->dtype), ival, pItem->imin, pItem->imax); @@ -417,7 +424,9 @@ int32_t cfgCheckRangeForDynUpdate(SConfig *pCfg, const char *name, const char *p } } break; case CFG_DTYPE_INT64: { - int64_t ival = (int64_t)taosStrHumanToInt64(pVal); + int64_t ival; + int32_t code = taosStrHumanToInt64(pVal, &ival); + if (code != TSDB_CODE_SUCCESS) return code; if (ival < pItem->imin || ival > pItem->imax) { uError("cfg:%s, type:%s value:%" PRId64 " out of range[%" PRId64 ", %" PRId64 "]", pItem->name, cfgDtypeStr(pItem->dtype), ival, pItem->imin, pItem->imax); @@ -427,9 +436,11 @@ int32_t cfgCheckRangeForDynUpdate(SConfig *pCfg, const char *name, const char *p } break; case CFG_DTYPE_FLOAT: case CFG_DTYPE_DOUBLE: { - float fval = (float)atof(pVal); - if (fval < pItem->fmin || fval > pItem->fmax) { - uError("cfg:%s, type:%s value:%f out of range[%f, %f]", pItem->name, cfgDtypeStr(pItem->dtype), fval, + double dval; + int32_t code = parseCfgReal(pVal, &dval); + if (code != TSDB_CODE_SUCCESS) return code; + if (dval < pItem->fmin || dval > pItem->fmax) { + uError("cfg:%s, type:%s value:%f out of range[%f, %f]", pItem->name, cfgDtypeStr(pItem->dtype), dval, pItem->fmin, pItem->fmax); terrno = TSDB_CODE_OUT_OF_RANGE; return -1; diff --git a/source/util/src/tunit.c b/source/util/src/tunit.c index 378f23613a..09f59f1e40 100644 --- a/source/util/src/tunit.c +++ b/source/util/src/tunit.c @@ -23,45 +23,74 @@ #define UNIT_ONE_PEBIBYTE (UNIT_ONE_TEBIBYTE * UNIT_SIZE_CONVERT_FACTOR) #define UNIT_ONE_EXBIBYTE (UNIT_ONE_PEBIBYTE * UNIT_SIZE_CONVERT_FACTOR) -int64_t taosStrHumanToInt64(const char* str) { - size_t sLen = strlen(str); - if (sLen < 2) return atoll(str); - - int64_t val = 0; - - char* strNoUnit = NULL; - char unit = str[sLen - 1]; - if ((unit == 'P') || (unit == 'p')) { - strNoUnit = taosMemoryCalloc(sLen, 1); - memcpy(strNoUnit, str, sLen - 1); - - val = atof(strNoUnit) * UNIT_ONE_PEBIBYTE; - } else if ((unit == 'T') || (unit == 't')) { - strNoUnit = taosMemoryCalloc(sLen, 1); - memcpy(strNoUnit, str, sLen - 1); - - val = atof(strNoUnit) * UNIT_ONE_TEBIBYTE; - } else if ((unit == 'G') || (unit == 'g')) { - strNoUnit = taosMemoryCalloc(sLen, 1); - memcpy(strNoUnit, str, sLen - 1); - - val = atof(strNoUnit) * UNIT_ONE_GIBIBYTE; - } else if ((unit == 'M') || (unit == 'm')) { - strNoUnit = taosMemoryCalloc(sLen, 1); - memcpy(strNoUnit, str, sLen - 1); - - val = atof(strNoUnit) * UNIT_ONE_MEBIBYTE; - } else if ((unit == 'K') || (unit == 'k')) { - strNoUnit = taosMemoryCalloc(sLen, 1); - memcpy(strNoUnit, str, sLen - 1); - - val = atof(strNoUnit) * UNIT_ONE_KIBIBYTE; - } else { - val = atoll(str); +static int32_t parseCfgIntWithUnit(const char* str, double *res) { + double val, temp = INT64_MAX; + char* endPtr; + errno = 0; + val = taosStr2Int64(str, &endPtr, 0); + if (*endPtr == '.' || errno == ERANGE) { + errno = 0; + val = taosStr2Double(str, &endPtr); } + if (endPtr == str || errno == ERANGE || isnan(val)) { + terrno = TSDB_CODE_INVALID_CFG_VALUE; + return -1; + } + while (isspace((unsigned char)*endPtr)) endPtr++; + uint64_t factor = 1; + if (*endPtr != '\0') { + switch (*endPtr) { + case 'P': + case 'p': { + temp /= UNIT_ONE_PEBIBYTE; + factor = UNIT_ONE_PEBIBYTE; + } break; + case 'T': + case 't': { + temp /= UNIT_ONE_TEBIBYTE; + factor = UNIT_ONE_TEBIBYTE; + } break; + case 'G': + case 'g': { + temp /= UNIT_ONE_GIBIBYTE; + factor = UNIT_ONE_GIBIBYTE; + } break; + case 'M': + case 'm': { + temp /= UNIT_ONE_MEBIBYTE; + factor = UNIT_ONE_MEBIBYTE; + } break; + case 'K': + case 'k': { + temp /= UNIT_ONE_KIBIBYTE; + factor = UNIT_ONE_KIBIBYTE; + } break; + default: + terrno = TSDB_CODE_INVALID_CFG_VALUE; + return -1; + } + if ((val > 0 && val > temp) || (val < 0 && val < -temp)) { + terrno = TSDB_CODE_OUT_OF_RANGE; + return -1; + } + endPtr++; + val *= factor; + } + while (isspace((unsigned char)*endPtr)) endPtr++; + if (*endPtr) { + terrno = TSDB_CODE_INVALID_CFG_VALUE; + return -1; + } + val = rint(val); + *res = val; + return TSDB_CODE_SUCCESS; +} - taosMemoryFree(strNoUnit); - return val; +int32_t taosStrHumanToInt64(const char* str, int64_t *out) { + double res; + int32_t code = parseCfgIntWithUnit(str, &res); + if (code == TSDB_CODE_SUCCESS) *out = (int64_t)res; + return code; } #ifdef BUILD_NO_CALL @@ -83,35 +112,17 @@ void taosInt64ToHumanStr(int64_t val, char* outStr) { } #endif -int32_t taosStrHumanToInt32(const char* str) { - size_t sLen = strlen(str); - if (sLen < 2) return atoll(str); - - int32_t val = 0; - - char* strNoUnit = NULL; - char unit = str[sLen - 1]; - if ((unit == 'G') || (unit == 'g')) { - strNoUnit = taosMemoryCalloc(sLen, 1); - memcpy(strNoUnit, str, sLen - 1); - - val = atof(strNoUnit) * UNIT_ONE_GIBIBYTE; - } else if ((unit == 'M') || (unit == 'm')) { - strNoUnit = taosMemoryCalloc(sLen, 1); - memcpy(strNoUnit, str, sLen - 1); - - val = atof(strNoUnit) * UNIT_ONE_MEBIBYTE; - } else if ((unit == 'K') || (unit == 'k')) { - strNoUnit = taosMemoryCalloc(sLen, 1); - memcpy(strNoUnit, str, sLen - 1); - - val = atof(strNoUnit) * UNIT_ONE_KIBIBYTE; - } else { - val = atoll(str); +int32_t taosStrHumanToInt32(const char* str, int32_t* out) { + double res; + int32_t code = parseCfgIntWithUnit(str, &res); + if (code == TSDB_CODE_SUCCESS) { + if (res < INT32_MIN || res > INT32_MAX) { + terrno = TSDB_CODE_OUT_OF_RANGE; + return -1; + } + *out = (int32_t)res; } - - taosMemoryFree(strNoUnit); - return val; + return code; } #ifdef BUILD_NO_CALL diff --git a/source/util/src/tutil.c b/source/util/src/tutil.c index 6b6878ec83..f201edcb5e 100644 --- a/source/util/src/tutil.c +++ b/source/util/src/tutil.c @@ -496,3 +496,21 @@ size_t twcsncspn(const TdUcs4 *wcs, size_t size, const TdUcs4 *reject, size_t rs return index; } + +int32_t parseCfgReal(const char* str, double* out) { + double val; + char *endPtr; + errno = 0; + val = taosStr2Double(str, &endPtr); + if (str == endPtr || errno == ERANGE || isnan(val)) { + terrno = TSDB_CODE_INVALID_CFG_VALUE; + return -1; + } + while(isspace((unsigned char)*endPtr)) endPtr++; + if (*endPtr != '\0') { + terrno = TSDB_CODE_INVALID_CFG_VALUE; + return -1; + } + *out = val; + return TSDB_CODE_SUCCESS; +} diff --git a/tests/system-test/0-others/show.py b/tests/system-test/0-others/show.py index 75d7116e03..bc1239fae8 100644 --- a/tests/system-test/0-others/show.py +++ b/tests/system-test/0-others/show.py @@ -240,6 +240,49 @@ class TDTestCase: self.show_create_sysdb_sql() self.show_create_systb_sql() self.show_column_name() + self.test_show_variables() + + def get_variable(self, name: str, local: bool = True): + if local: + sql = 'show local variables' + else: + sql = f'select `value` from information_schema.ins_dnode_variables where name like "{name}"' + tdSql.query(sql, queryTimes=1) + res = tdSql.queryResult + if local: + for row in res: + if row[0] == name: + return row[1] + else: + if len(res) > 0: + return res[0][0] + raise Exception(f"variable {name} not found") + + def test_show_variables(self): + epsion = 0.0000001 + var = 'minimalTmpDirGB' + expect_val: float = 10.11 + sql = f'ALTER LOCAL "{var}" "{expect_val}"' + tdSql.execute(sql) + val: float = float(self.get_variable(var)) + if val != expect_val: + tdLog.exit(f'failed to set local {var} to {expect_val} actually {val}') + + error_vals = ['a', '10a', '', '1.100r', '1.12 r'] + for error_val in error_vals: + tdSql.error(f'ALTER LOCAL "{var}" "{error_val}"') + + var = 'supportVnodes' + expect_val = 1240 ## 1.211111 * 1024 + sql = f'ALTER DNODE 1 "{var}" "1.211111k"' + tdSql.execute(sql, queryTimes=1) + val = int(self.get_variable(var, False)) + if val != expect_val: + tdLog.exit(f'failed to set dnode {var} to {expect_val} actually {val}') + + error_vals = ['a', '10a', '', '1.100r', '1.12 r', '5k'] + for error_val in error_vals: + tdSql.error(f'ALTER DNODE 1 "{var}" "{error_val}"') def stop(self): tdSql.close()