From 7e8f3579de3d3e37c9bf9173227bf74bd74f63c4 Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Thu, 30 Nov 2023 21:06:06 +0800 Subject: [PATCH 01/10] accept float data as int data input --- include/common/ttypes.h | 2 + include/common/tvariant.h | 3 + source/common/src/tvariant.c | 190 ++++++++++++++++++++++++-- source/common/test/commonTests.cpp | 94 ++++++++++++- source/libs/parser/src/parInsertSql.c | 50 ++++--- 5 files changed, 309 insertions(+), 30 deletions(-) diff --git a/include/common/ttypes.h b/include/common/ttypes.h index 279799b172..741e3663db 100644 --- a/include/common/ttypes.h +++ b/include/common/ttypes.h @@ -275,9 +275,11 @@ typedef struct { #define IS_VALID_TINYINT(_t) ((_t) >= INT8_MIN && (_t) <= INT8_MAX) #define IS_VALID_SMALLINT(_t) ((_t) >= INT16_MIN && (_t) <= INT16_MAX) #define IS_VALID_INT(_t) ((_t) >= INT32_MIN && (_t) <= INT32_MAX) +#define IS_VALID_INT64(_t) ((_t) >= INT64_MIN && (_t) <= INT64_MAX) #define IS_VALID_UTINYINT(_t) ((_t) >= 0 && (_t) <= UINT8_MAX) #define IS_VALID_USMALLINT(_t) ((_t) >= 0 && (_t) <= UINT16_MAX) #define IS_VALID_UINT(_t) ((_t) >= 0 && (_t) <= UINT32_MAX) +#define IS_VALID_UINT64(_t) ((_t) >= 0 && (_t) <= UINT64_MAX) #define IS_VALID_FLOAT(_t) ((_t) >= -FLT_MAX && (_t) <= FLT_MAX) #define IS_VALID_DOUBLE(_t) ((_t) >= -DBL_MAX && (_t) <= DBL_MAX) diff --git a/include/common/tvariant.h b/include/common/tvariant.h index 130945cce5..61cf277bb7 100644 --- a/include/common/tvariant.h +++ b/include/common/tvariant.h @@ -37,6 +37,9 @@ typedef struct SVariant { }; } SVariant; +int32_t toIntegerEx(const char *z, int32_t n, int64_t *value); +int32_t toUIntegerEx(const char *z, int32_t n, uint64_t *value); + int32_t toInteger(const char *z, int32_t n, int32_t base, int64_t *value); int32_t toUInteger(const char *z, int32_t n, int32_t base, uint64_t *value); diff --git a/source/common/src/tvariant.c b/source/common/src/tvariant.c index 5f2796260c..adb0964fe6 100644 --- a/source/common/src/tvariant.c +++ b/source/common/src/tvariant.c @@ -19,6 +19,181 @@ #include "ttokendef.h" #include "tvariant.h" +int32_t parseBinaryUInteger(const char *z, int32_t n, uint64_t *value) { + // skip head 0b + const char *p = z + 2; + int32_t l = n - 2; + + while (*p == '0' && l > 0) { + p++; + l--; + } + if (l > 64) { // too big + return TSDB_CODE_FAILED; + } + + uint64_t val = 0; + for (int32_t i = 0; i < l; i++) { + val = val << 1; + if (p[i] == '1') { + val |= 1; + } else if (p[i] != '0') { // abnormal char + return TSDB_CODE_FAILED; + } + } + *value = val; + return TSDB_CODE_SUCCESS; +} + +int32_t parseDecimalUInteger(const char *z, int32_t n, uint64_t *value) { + errno = 0; + char *endPtr = NULL; + while (*z == '0' && n > 1) { + z++; + n--; + } + *value = taosStr2UInt64(z, &endPtr, 10); + if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { + return TSDB_CODE_FAILED; + } + + return TSDB_CODE_SUCCESS; +} + +int32_t parseHexUInteger(const char *z, int32_t n, uint64_t *value) { + errno = 0; + char *endPtr = NULL; + *value = taosStr2UInt64(z, &endPtr, 16); + if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { + return TSDB_CODE_FAILED; + } + return TSDB_CODE_SUCCESS; +} + +int32_t parseSignAndUInteger(const char *z, int32_t n, bool *is_neg, uint64_t *value) { + *is_neg = false; + if (n < 1) { + return TSDB_CODE_FAILED; + } + + // parse sign + bool has_sign = false; + if (z[0] == '-') { + *is_neg = true; + has_sign = true; + } else if (z[0] == '+') { + has_sign = true; + } else if (z[0] < '0' || z[0] > '9') { + return TSDB_CODE_FAILED; + } + if (has_sign) { + z++; + n--; + if (n < 1) { + return TSDB_CODE_FAILED; + } + } + + if (n > 2 && z[0] == '0') { + if (z[1] == 'b' || z[1] == 'B') { + // paring as binary + return parseBinaryUInteger(z, n, value); + } + + if (z[1] == 'x' || z[1] == 'X') { + // parsing as hex + return parseHexUInteger(z, n, value); + } + } + + //rm flag u-unsigned, l-long, f-float(if not in hex str) + char last = tolower(z[n-1]); + if (last == 'u' || last == 'l' || last == 'f') { + n--; + if (n < 1) { + return TSDB_CODE_FAILED; + } + } + + // parsing as decimal + bool parse_int = true; + for (int32_t i = 0; i < n; i++) { + if (z[i] < '0' || z[i] > '9') { + parse_int = false; + break; + } + } + if (parse_int) { + return parseDecimalUInteger(z, n, value); + } + + // parsing as double + errno = 0; + char *endPtr = NULL; + double val = taosStr2Double(z, &endPtr); + if (errno == ERANGE || errno == EINVAL || endPtr - z != n || !IS_VALID_UINT64(val)) { + return TSDB_CODE_FAILED; + } + *value = val; + return TSDB_CODE_SUCCESS; +} + +int32_t removeSpace(const char **pp, int32_t n) { + // rm blank space from both head and tail + const char *z = *pp; + while (*z == ' ' && n > 0) { + z++; + n--; + } + if (n > 0) { + for (int32_t i = n - 1; i > 0; i--) { + if (z[i] == ' ') { + n--; + } else { + break; + } + } + } + *pp = z; + return n; +} + +int32_t toIntegerEx(const char *z, int32_t n, int64_t *value) { + n = removeSpace(&z, n); + if (n < 1) { // fail: all char is space + return TSDB_CODE_FAILED; + } + + bool is_neg = false; + uint64_t uv = 0; + int32_t code = parseSignAndUInteger(z, n, &is_neg, &uv); + if (code == TSDB_CODE_SUCCESS) { + // truncate into int64 + if (uv > INT64_MAX) { + *value = is_neg ? INT64_MIN : INT64_MAX; + return TSDB_CODE_FAILED; + } else { + *value = is_neg ? -uv : uv; + } + } + + return code; +} + +int32_t toUIntegerEx(const char *z, int32_t n, uint64_t *value) { + n = removeSpace(&z, n); + if (n < 1) { // fail: all char is space + return TSDB_CODE_FAILED; + } + + bool is_neg = false; + int32_t code = parseSignAndUInteger(z, n, &is_neg, value); + if (is_neg) { + return TSDB_CODE_FAILED; + } + return code; +} + int32_t toInteger(const char *z, int32_t n, int32_t base, int64_t *value) { errno = 0; char *endPtr = NULL; @@ -26,10 +201,10 @@ int32_t toInteger(const char *z, int32_t n, int32_t base, int64_t *value) { *value = taosStr2Int64(z, &endPtr, base); if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { errno = 0; - return -1; + return TSDB_CODE_FAILED; } - return 0; + return TSDB_CODE_SUCCESS; } int32_t toUInteger(const char *z, int32_t n, int32_t base, uint64_t *value) { @@ -39,16 +214,15 @@ int32_t toUInteger(const char *z, int32_t n, int32_t base, uint64_t *value) { const char *p = z; while (*p == ' ') p++; if (*p == '-') { - return -1; + return TSDB_CODE_FAILED; } *value = taosStr2UInt64(z, &endPtr, base); if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { errno = 0; - return -1; + return TSDB_CODE_FAILED; } - - return 0; + return TSDB_CODE_SUCCESS; } /** @@ -147,14 +321,13 @@ void taosVariantDestroy(SVariant *pVar) { taosMemoryFreeClear(pVar->pz); pVar->nLen = 0; } - } void taosVariantAssign(SVariant *pDst, const SVariant *pSrc) { if (pSrc == NULL || pDst == NULL) return; pDst->nType = pSrc->nType; - if (pSrc->nType == TSDB_DATA_TYPE_BINARY ||pSrc->nType == TSDB_DATA_TYPE_VARBINARY || + if (pSrc->nType == TSDB_DATA_TYPE_BINARY || pSrc->nType == TSDB_DATA_TYPE_VARBINARY || pSrc->nType == TSDB_DATA_TYPE_NCHAR || pSrc->nType == TSDB_DATA_TYPE_JSON || pSrc->nType == TSDB_DATA_TYPE_GEOMETRY) { int32_t len = pSrc->nLen + TSDB_NCHAR_SIZE; @@ -172,7 +345,6 @@ void taosVariantAssign(SVariant *pDst, const SVariant *pSrc) { if (IS_NUMERIC_TYPE(pSrc->nType) || (pSrc->nType == TSDB_DATA_TYPE_BOOL)) { pDst->i = pSrc->i; } - } int32_t taosVariantCompare(const SVariant *p1, const SVariant *p2) { diff --git a/source/common/test/commonTests.cpp b/source/common/test/commonTests.cpp index 107276d7f9..035bca0e15 100644 --- a/source/common/test/commonTests.cpp +++ b/source/common/test/commonTests.cpp @@ -24,6 +24,93 @@ int main(int argc, char** argv) { return RUN_ALL_TESTS(); } + +TEST(testCase, toIntegerEx_test) { + int64_t val = 0; + + char* s = "123"; + int32_t ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 123); + + s = "9223372036854775807"; + ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 9223372036854775807); + + s = "9323372036854775807"; + ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, -1); + + s = "-9323372036854775807"; + ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, -1); + + s = "-1"; + ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, -1); + + s = "-9223372036854775807"; + ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, -9223372036854775807); + + s = "1000u"; + ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 1000); + + s = "1000l"; + ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 1000); + + s = "1000.0f"; + ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 1000); + + s = "0x1f"; + ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 31); + + s = "-0x40"; + ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, -64); + + s = "0b110"; + ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 6); + + s = "-0b10010"; + ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, -18); + + s = "-5.2343544534e10"; + ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, -52343544534); + + s = "1.869895343e4"; + ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 18698); + + // UINT64_MAX + s = "18446744073709551615"; + ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, -1); + + s = "18446744073709551616"; + ret = toIntegerEx(s, strlen(s), &val); + ASSERT_EQ(ret, -1); +} + TEST(testCase, toInteger_test) { char* s = "123"; uint32_t type = 0; @@ -39,10 +126,9 @@ TEST(testCase, toInteger_test) { ASSERT_EQ(ret, 0); ASSERT_EQ(val, 9223372036854775807); - s = "9323372036854775807"; + s = "9323372036854775807"; // out of range, > int64_max ret = toInteger(s, strlen(s), 10, &val); - ASSERT_EQ(ret, 0); - ASSERT_EQ(val, 9323372036854775807u); + ASSERT_EQ(ret, -1); s = "-9323372036854775807"; ret = toInteger(s, strlen(s), 10, &val); @@ -139,7 +225,7 @@ TEST(testCase, Datablock_test) { printf("binary column length:%d\n", *(int32_t*)p1->pData); - ASSERT_EQ(blockDataGetNumOfCols(b), 2); + ASSERT_EQ(blockDataGetNumOfCols(b), 3); ASSERT_EQ(blockDataGetNumOfRows(b), 40); char* pData = colDataGetData(p1, 3); diff --git a/source/libs/parser/src/parInsertSql.c b/source/libs/parser/src/parInsertSql.c index 31b016458a..c993efb9ae 100644 --- a/source/libs/parser/src/parInsertSql.c +++ b/source/libs/parser/src/parInsertSql.c @@ -433,7 +433,8 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, } case TSDB_DATA_TYPE_TINYINT: { - if (TSDB_CODE_SUCCESS != toInteger(pToken->z, pToken->n, 10, &iv)) { + code = toIntegerEx(pToken->z, pToken->n, &iv); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "invalid tinyint data", pToken->z); } else if (!IS_VALID_TINYINT(iv)) { return buildSyntaxErrMsg(pMsgBuf, "tinyint data overflow", pToken->z); @@ -444,7 +445,8 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, } case TSDB_DATA_TYPE_UTINYINT: { - if (TSDB_CODE_SUCCESS != toUInteger(pToken->z, pToken->n, 10, &uv)) { + code = toUIntegerEx(pToken->z, pToken->n, &uv); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "invalid unsigned tinyint data", pToken->z); } else if (uv > UINT8_MAX) { return buildSyntaxErrMsg(pMsgBuf, "unsigned tinyint data overflow", pToken->z); @@ -454,7 +456,8 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, } case TSDB_DATA_TYPE_SMALLINT: { - if (TSDB_CODE_SUCCESS != toInteger(pToken->z, pToken->n, 10, &iv)) { + code = toIntegerEx(pToken->z, pToken->n, &iv); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "invalid smallint data", pToken->z); } else if (!IS_VALID_SMALLINT(iv)) { return buildSyntaxErrMsg(pMsgBuf, "smallint data overflow", pToken->z); @@ -464,7 +467,8 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, } case TSDB_DATA_TYPE_USMALLINT: { - if (TSDB_CODE_SUCCESS != toUInteger(pToken->z, pToken->n, 10, &uv)) { + code = toUIntegerEx(pToken->z, pToken->n, &uv); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "invalid unsigned smallint data", pToken->z); } else if (uv > UINT16_MAX) { return buildSyntaxErrMsg(pMsgBuf, "unsigned smallint data overflow", pToken->z); @@ -474,7 +478,8 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, } case TSDB_DATA_TYPE_INT: { - if (TSDB_CODE_SUCCESS != toInteger(pToken->z, pToken->n, 10, &iv)) { + code = toIntegerEx(pToken->z, pToken->n, &iv); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "invalid int data", pToken->z); } else if (!IS_VALID_INT(iv)) { return buildSyntaxErrMsg(pMsgBuf, "int data overflow", pToken->z); @@ -484,7 +489,8 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, } case TSDB_DATA_TYPE_UINT: { - if (TSDB_CODE_SUCCESS != toUInteger(pToken->z, pToken->n, 10, &uv)) { + code = toUIntegerEx(pToken->z, pToken->n, &uv); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "invalid unsigned int data", pToken->z); } else if (uv > UINT32_MAX) { return buildSyntaxErrMsg(pMsgBuf, "unsigned int data overflow", pToken->z); @@ -494,7 +500,8 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, } case TSDB_DATA_TYPE_BIGINT: { - if (TSDB_CODE_SUCCESS != toInteger(pToken->z, pToken->n, 10, &iv)) { + code = toIntegerEx(pToken->z, pToken->n, &iv); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "invalid bigint data", pToken->z); } val->i64 = iv; @@ -502,7 +509,8 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, } case TSDB_DATA_TYPE_UBIGINT: { - if (TSDB_CODE_SUCCESS != toUInteger(pToken->z, pToken->n, 10, &uv)) { + code = toUIntegerEx(pToken->z, pToken->n, &uv); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "invalid unsigned bigint data", pToken->z); } *(uint64_t*)(&val->i64) = uv; @@ -1351,7 +1359,8 @@ static int32_t parseValueTokenImpl(SInsertParseContext* pCxt, const char** pSql, break; } case TSDB_DATA_TYPE_TINYINT: { - if (TSDB_CODE_SUCCESS != toInteger(pToken->z, pToken->n, 10, &pVal->value.val)) { + int32_t code = toIntegerEx(pToken->z, pToken->n, &pVal->value.val); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(&pCxt->msg, "invalid tinyint data", pToken->z); } else if (!IS_VALID_TINYINT(pVal->value.val)) { return buildSyntaxErrMsg(&pCxt->msg, "tinyint data overflow", pToken->z); @@ -1359,7 +1368,8 @@ static int32_t parseValueTokenImpl(SInsertParseContext* pCxt, const char** pSql, break; } case TSDB_DATA_TYPE_UTINYINT: { - if (TSDB_CODE_SUCCESS != toUInteger(pToken->z, pToken->n, 10, &pVal->value.val)) { + int32_t code = toUIntegerEx(pToken->z, pToken->n, &pVal->value.val); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(&pCxt->msg, "invalid unsigned tinyint data", pToken->z); } else if (pVal->value.val > UINT8_MAX) { return buildSyntaxErrMsg(&pCxt->msg, "unsigned tinyint data overflow", pToken->z); @@ -1367,7 +1377,8 @@ static int32_t parseValueTokenImpl(SInsertParseContext* pCxt, const char** pSql, break; } case TSDB_DATA_TYPE_SMALLINT: { - if (TSDB_CODE_SUCCESS != toInteger(pToken->z, pToken->n, 10, &pVal->value.val)) { + int32_t code = toIntegerEx(pToken->z, pToken->n, &pVal->value.val); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(&pCxt->msg, "invalid smallint data", pToken->z); } else if (!IS_VALID_SMALLINT(pVal->value.val)) { return buildSyntaxErrMsg(&pCxt->msg, "smallint data overflow", pToken->z); @@ -1375,7 +1386,8 @@ static int32_t parseValueTokenImpl(SInsertParseContext* pCxt, const char** pSql, break; } case TSDB_DATA_TYPE_USMALLINT: { - if (TSDB_CODE_SUCCESS != toUInteger(pToken->z, pToken->n, 10, &pVal->value.val)) { + int32_t code = toUIntegerEx(pToken->z, pToken->n, &pVal->value.val); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(&pCxt->msg, "invalid unsigned smallint data", pToken->z); } else if (pVal->value.val > UINT16_MAX) { return buildSyntaxErrMsg(&pCxt->msg, "unsigned smallint data overflow", pToken->z); @@ -1383,7 +1395,8 @@ static int32_t parseValueTokenImpl(SInsertParseContext* pCxt, const char** pSql, break; } case TSDB_DATA_TYPE_INT: { - if (TSDB_CODE_SUCCESS != toInteger(pToken->z, pToken->n, 10, &pVal->value.val)) { + int32_t code = toIntegerEx(pToken->z, pToken->n, &pVal->value.val); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(&pCxt->msg, "invalid int data", pToken->z); } else if (!IS_VALID_INT(pVal->value.val)) { return buildSyntaxErrMsg(&pCxt->msg, "int data overflow", pToken->z); @@ -1391,7 +1404,8 @@ static int32_t parseValueTokenImpl(SInsertParseContext* pCxt, const char** pSql, break; } case TSDB_DATA_TYPE_UINT: { - if (TSDB_CODE_SUCCESS != toUInteger(pToken->z, pToken->n, 10, &pVal->value.val)) { + int32_t code = toUIntegerEx(pToken->z, pToken->n, &pVal->value.val); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(&pCxt->msg, "invalid unsigned int data", pToken->z); } else if (pVal->value.val > UINT32_MAX) { return buildSyntaxErrMsg(&pCxt->msg, "unsigned int data overflow", pToken->z); @@ -1399,14 +1413,16 @@ static int32_t parseValueTokenImpl(SInsertParseContext* pCxt, const char** pSql, break; } case TSDB_DATA_TYPE_BIGINT: { - if (TSDB_CODE_SUCCESS != toInteger(pToken->z, pToken->n, 10, &pVal->value.val)) { + int32_t code = toIntegerEx(pToken->z, pToken->n, &pVal->value.val); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(&pCxt->msg, "invalid bigint data", pToken->z); } break; } case TSDB_DATA_TYPE_UBIGINT: { - if (TSDB_CODE_SUCCESS != toUInteger(pToken->z, pToken->n, 10, &pVal->value.val)) { - return buildSyntaxErrMsg(&pCxt->msg, "invalid unsigned bigint data", pToken->z); + int32_t code = toUIntegerEx(pToken->z, pToken->n, &pVal->value.val); + if (TSDB_CODE_SUCCESS != code) { + return buildSyntaxErrMsg(&pCxt->msg, "invalid unsigned int data", pToken->z); } break; } From d2b2dee256812085a464ba3d170779f106e46efd Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Fri, 1 Dec 2023 23:44:05 +0800 Subject: [PATCH 02/10] opt & add test case --- include/common/tvariant.h | 5 +- source/common/src/tvariant.c | 185 +++++++++++------- source/common/test/commonTests.cpp | 203 +++++++++++++------- source/libs/parser/src/parInsertSql.c | 60 +++--- source/libs/parser/src/parTokenizer.c | 7 +- tests/parallel_test/cases.task | 1 + tests/system-test/1-insert/insert_double.py | 133 +++++++++++++ 7 files changed, 422 insertions(+), 172 deletions(-) create mode 100644 tests/system-test/1-insert/insert_double.py diff --git a/include/common/tvariant.h b/include/common/tvariant.h index 61cf277bb7..66b7302f4e 100644 --- a/include/common/tvariant.h +++ b/include/common/tvariant.h @@ -37,8 +37,9 @@ typedef struct SVariant { }; } SVariant; -int32_t toIntegerEx(const char *z, int32_t n, int64_t *value); -int32_t toUIntegerEx(const char *z, int32_t n, uint64_t *value); +int32_t toIntegerEx(const char *z, int32_t n, uint32_t type, int64_t *value); +int32_t toUIntegerEx(const char *z, int32_t n, uint32_t type, uint64_t *value); +int32_t toDoubleEx(const char *z, int32_t n, uint32_t type, double *value); int32_t toInteger(const char *z, int32_t n, int32_t base, int64_t *value); int32_t toUInteger(const char *z, int32_t n, int32_t base, uint64_t *value); diff --git a/source/common/src/tvariant.c b/source/common/src/tvariant.c index adb0964fe6..609bcf1382 100644 --- a/source/common/src/tvariant.c +++ b/source/common/src/tvariant.c @@ -45,21 +45,6 @@ int32_t parseBinaryUInteger(const char *z, int32_t n, uint64_t *value) { return TSDB_CODE_SUCCESS; } -int32_t parseDecimalUInteger(const char *z, int32_t n, uint64_t *value) { - errno = 0; - char *endPtr = NULL; - while (*z == '0' && n > 1) { - z++; - n--; - } - *value = taosStr2UInt64(z, &endPtr, 10); - if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { - return TSDB_CODE_FAILED; - } - - return TSDB_CODE_SUCCESS; -} - int32_t parseHexUInteger(const char *z, int32_t n, uint64_t *value) { errno = 0; char *endPtr = NULL; @@ -71,10 +56,6 @@ int32_t parseHexUInteger(const char *z, int32_t n, uint64_t *value) { } int32_t parseSignAndUInteger(const char *z, int32_t n, bool *is_neg, uint64_t *value) { - *is_neg = false; - if (n < 1) { - return TSDB_CODE_FAILED; - } // parse sign bool has_sign = false; @@ -83,18 +64,18 @@ int32_t parseSignAndUInteger(const char *z, int32_t n, bool *is_neg, uint64_t *v has_sign = true; } else if (z[0] == '+') { has_sign = true; - } else if (z[0] < '0' || z[0] > '9') { + } else if (z[0] != '.' && (z[0] < '0' || z[0] > '9')) { return TSDB_CODE_FAILED; } if (has_sign) { - z++; - n--; - if (n < 1) { + if (n < 2) { return TSDB_CODE_FAILED; } + z++; + n--; } - if (n > 2 && z[0] == '0') { + if (z[0] == '0' && n > 2) { if (z[1] == 'b' || z[1] == 'B') { // paring as binary return parseBinaryUInteger(z, n, value); @@ -106,89 +87,157 @@ int32_t parseSignAndUInteger(const char *z, int32_t n, bool *is_neg, uint64_t *v } } - //rm flag u-unsigned, l-long, f-float(if not in hex str) - char last = tolower(z[n-1]); - if (last == 'u' || last == 'l' || last == 'f') { - n--; - if (n < 1) { - return TSDB_CODE_FAILED; - } - } - - // parsing as decimal - bool parse_int = true; - for (int32_t i = 0; i < n; i++) { - if (z[i] < '0' || z[i] > '9') { - parse_int = false; - break; - } - } - if (parse_int) { - return parseDecimalUInteger(z, n, value); - } - // parsing as double errno = 0; char *endPtr = NULL; double val = taosStr2Double(z, &endPtr); - if (errno == ERANGE || errno == EINVAL || endPtr - z != n || !IS_VALID_UINT64(val)) { + if (errno == ERANGE || errno == EINVAL || endPtr - z != n || val > UINT64_MAX) { return TSDB_CODE_FAILED; } - *value = val; + *value = round(val); return TSDB_CODE_SUCCESS; } int32_t removeSpace(const char **pp, int32_t n) { - // rm blank space from both head and tail + // rm blank space from both head and tail, keep at least one char const char *z = *pp; - while (*z == ' ' && n > 0) { + while (n > 1 && *z == ' ') { z++; n--; } - if (n > 0) { - for (int32_t i = n - 1; i > 0; i--) { - if (z[i] == ' ') { - n--; - } else { - break; - } - } + while (n > 1 && z[n-1] == ' ') { + n--; } *pp = z; return n; } -int32_t toIntegerEx(const char *z, int32_t n, int64_t *value) { - n = removeSpace(&z, n); - if (n < 1) { // fail: all char is space +int32_t toDoubleEx(const char *z, int32_t n, uint32_t type, double* value) { + if (type != TK_NK_FLOAT) { + if (n == 0) { + *value = 0; + return TSDB_CODE_SUCCESS; + } + // rm tail space + while (n > 1 && z[n-1] == ' ') { + n--; + } + } + + errno = 0; + char* endPtr = NULL; + *value = taosStr2Double(z, &endPtr); + + if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { return TSDB_CODE_FAILED; } + return TSDB_CODE_SUCCESS; +} + +int32_t toIntegerEx(const char *z, int32_t n, uint32_t type, int64_t *value) { + errno = 0; + char *endPtr = NULL; + switch (type) + { + case TK_NK_INTEGER: { + *value = taosStr2Int64(z, &endPtr, 10); + if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { + return TSDB_CODE_FAILED; + } + return TSDB_CODE_SUCCESS; + } + case TK_NK_HEX: { + *value = taosStr2Int64(z, &endPtr, 16); + if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { + return TSDB_CODE_FAILED; + } + return TSDB_CODE_SUCCESS; + } + case TK_NK_BIN: { + *value = taosStr2Int64(z, &endPtr, 2); + if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { + return TSDB_CODE_FAILED; + } + return TSDB_CODE_SUCCESS; + } + default: + break; + } + + // parse string + if (n == 0) { + *value = 0; + return TSDB_CODE_SUCCESS; + } + n = removeSpace(&z, n); bool is_neg = false; uint64_t uv = 0; int32_t code = parseSignAndUInteger(z, n, &is_neg, &uv); if (code == TSDB_CODE_SUCCESS) { // truncate into int64 - if (uv > INT64_MAX) { - *value = is_neg ? INT64_MIN : INT64_MAX; - return TSDB_CODE_FAILED; + if (is_neg) { + if (uv > 1ull + INT64_MAX) { + *value = INT64_MIN; + return TSDB_CODE_FAILED; + } else { + *value = -uv; + } } else { - *value = is_neg ? -uv : uv; + if (uv > INT64_MAX) { + *value = INT64_MAX; + return TSDB_CODE_FAILED; + } + *value = uv; } } return code; } -int32_t toUIntegerEx(const char *z, int32_t n, uint64_t *value) { - n = removeSpace(&z, n); - if (n < 1) { // fail: all char is space - return TSDB_CODE_FAILED; +int32_t toUIntegerEx(const char *z, int32_t n, uint32_t type, uint64_t *value) { + errno = 0; + char *endPtr = NULL; + switch (type) + { + case TK_NK_INTEGER: { + *value = taosStr2UInt64(z, &endPtr, 10); + if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { + return TSDB_CODE_FAILED; + } + return TSDB_CODE_SUCCESS; + } + case TK_NK_HEX: { + *value = taosStr2UInt64(z, &endPtr, 16); + if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { + return TSDB_CODE_FAILED; + } + return TSDB_CODE_SUCCESS; + } + case TK_NK_BIN: { + *value = taosStr2UInt64(z, &endPtr, 2); + if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { + return TSDB_CODE_FAILED; + } + return TSDB_CODE_SUCCESS; + } + default: + break; } + // parse string + if (n == 0) { + *value = 0; + return TSDB_CODE_SUCCESS; + } + n = removeSpace(&z, n); + bool is_neg = false; int32_t code = parseSignAndUInteger(z, n, &is_neg, value); if (is_neg) { + if (TSDB_CODE_SUCCESS == code && 0 == *value) { + return TSDB_CODE_SUCCESS; + } return TSDB_CODE_FAILED; } return code; diff --git a/source/common/test/commonTests.cpp b/source/common/test/commonTests.cpp index 035bca0e15..291941d9b2 100644 --- a/source/common/test/commonTests.cpp +++ b/source/common/test/commonTests.cpp @@ -14,6 +14,7 @@ #include "tdef.h" #include "tvariant.h" #include "ttime.h" +#include "ttokendef.h" namespace { // @@ -25,125 +26,176 @@ int main(int argc, char** argv) { } +TEST(testCase, toUIntegerEx_test) { + uint64_t val = 0; + + char* s = "123"; + int32_t ret = toUIntegerEx(s, strlen(s), TK_NK_INTEGER, &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 123); + + s = "1000u"; + ret = toUIntegerEx(s, strlen(s), 0, &val); + ASSERT_EQ(ret, -1); + + s = "0x1f"; + ret = toUIntegerEx(s, strlen(s), TK_NK_HEX, &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 31); + + s = "0b110"; + ret = toUIntegerEx(s, strlen(s), 0, &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 6); + + s = "2567.4787"; + ret = toUIntegerEx(s, strlen(s), TK_NK_FLOAT, &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 2567); + + s = "1.869895343e4"; + ret = toUIntegerEx(s, strlen(s), TK_NK_FLOAT, &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 18699); + + s = "-1"; + ret = toUIntegerEx(s, strlen(s),TK_NK_INTEGER, &val); + ASSERT_EQ(ret, -1); + + s = "-0b10010"; + ret = toUIntegerEx(s, strlen(s), 0, &val); + ASSERT_EQ(ret, -1); + + s = "-0x40"; + ret = toUIntegerEx(s, strlen(s), TK_NK_HEX, &val); + ASSERT_EQ(ret, -1); + + s = "-80.9999"; + ret = toUIntegerEx(s, strlen(s), TK_NK_FLOAT, &val); + ASSERT_EQ(ret, -1); + + s = "-5.2343544534e10"; + ret = toUIntegerEx(s, strlen(s), TK_NK_FLOAT, &val); + ASSERT_EQ(ret, -1); + + // INT64_MAX + s = "9223372036854775807"; + ret = toUIntegerEx(s, strlen(s), TK_NK_INTEGER, &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 9223372036854775807); + + // UINT64_MAX + s = "18446744073709551615"; + ret = toUIntegerEx(s, strlen(s), TK_NK_INTEGER, &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 18446744073709551615u); + + // out of range + s = "18446744073709551616"; + ret = toUIntegerEx(s, strlen(s), TK_NK_INTEGER, &val); + ASSERT_EQ(ret, -1); + + s = "5.23e25"; + ret = toUIntegerEx(s, strlen(s), TK_NK_FLOAT, &val); + ASSERT_EQ(ret, -1); +} + TEST(testCase, toIntegerEx_test) { int64_t val = 0; char* s = "123"; - int32_t ret = toIntegerEx(s, strlen(s), &val); + int32_t ret = toIntegerEx(s, strlen(s), TK_NK_INTEGER, &val); ASSERT_EQ(ret, 0); ASSERT_EQ(val, 123); - s = "9223372036854775807"; - ret = toIntegerEx(s, strlen(s), &val); - ASSERT_EQ(ret, 0); - ASSERT_EQ(val, 9223372036854775807); - - s = "9323372036854775807"; - ret = toIntegerEx(s, strlen(s), &val); - ASSERT_EQ(ret, -1); - - s = "-9323372036854775807"; - ret = toIntegerEx(s, strlen(s), &val); - ASSERT_EQ(ret, -1); - s = "-1"; - ret = toIntegerEx(s, strlen(s), &val); + ret = toIntegerEx(s, strlen(s), TK_NK_INTEGER, &val); ASSERT_EQ(ret, 0); ASSERT_EQ(val, -1); - s = "-9223372036854775807"; - ret = toIntegerEx(s, strlen(s), &val); - ASSERT_EQ(ret, 0); - ASSERT_EQ(val, -9223372036854775807); - s = "1000u"; - ret = toIntegerEx(s, strlen(s), &val); - ASSERT_EQ(ret, 0); - ASSERT_EQ(val, 1000); - - s = "1000l"; - ret = toIntegerEx(s, strlen(s), &val); - ASSERT_EQ(ret, 0); - ASSERT_EQ(val, 1000); - - s = "1000.0f"; - ret = toIntegerEx(s, strlen(s), &val); - ASSERT_EQ(ret, 0); - ASSERT_EQ(val, 1000); + ret = toIntegerEx(s, strlen(s), TK_NK_INTEGER, &val); + ASSERT_EQ(ret, -1); s = "0x1f"; - ret = toIntegerEx(s, strlen(s), &val); + ret = toIntegerEx(s, strlen(s), TK_NK_HEX, &val); ASSERT_EQ(ret, 0); ASSERT_EQ(val, 31); s = "-0x40"; - ret = toIntegerEx(s, strlen(s), &val); + ret = toIntegerEx(s, strlen(s), TK_NK_HEX, &val); ASSERT_EQ(ret, 0); ASSERT_EQ(val, -64); s = "0b110"; - ret = toIntegerEx(s, strlen(s), &val); + ret = toIntegerEx(s, strlen(s), 0, &val); ASSERT_EQ(ret, 0); ASSERT_EQ(val, 6); s = "-0b10010"; - ret = toIntegerEx(s, strlen(s), &val); + ret = toIntegerEx(s, strlen(s), 0, &val); ASSERT_EQ(ret, 0); ASSERT_EQ(val, -18); + s = "-80.9999"; + ret = toIntegerEx(s, strlen(s), TK_NK_FLOAT, &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, -81); + + s = "2567.8787"; + ret = toIntegerEx(s, strlen(s), TK_NK_FLOAT, &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 2568); + s = "-5.2343544534e10"; - ret = toIntegerEx(s, strlen(s), &val); + ret = toIntegerEx(s, strlen(s), TK_NK_FLOAT, &val); ASSERT_EQ(ret, 0); ASSERT_EQ(val, -52343544534); s = "1.869895343e4"; - ret = toIntegerEx(s, strlen(s), &val); + ret = toIntegerEx(s, strlen(s), TK_NK_FLOAT, &val); ASSERT_EQ(ret, 0); - ASSERT_EQ(val, 18698); + ASSERT_EQ(val, 18699); + + // INT64_MAX + s = "9223372036854775807"; + ret = toIntegerEx(s, strlen(s), TK_NK_INTEGER, &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, 9223372036854775807LL); + + s = "-9223372036854775808"; + ret = toIntegerEx(s, strlen(s), TK_NK_INTEGER, &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, -9223372036854775808); + + // out of range + s = "9323372036854775807"; + ret = toIntegerEx(s, strlen(s), TK_NK_INTEGER, &val); + ASSERT_EQ(ret, -1); + + s = "-9323372036854775807"; + ret = toIntegerEx(s, strlen(s), TK_NK_INTEGER, &val); + ASSERT_EQ(ret, -1); // UINT64_MAX s = "18446744073709551615"; - ret = toIntegerEx(s, strlen(s), &val); - ASSERT_EQ(ret, -1); - - s = "18446744073709551616"; - ret = toIntegerEx(s, strlen(s), &val); + ret = toIntegerEx(s, strlen(s), TK_NK_INTEGER, &val); ASSERT_EQ(ret, -1); } TEST(testCase, toInteger_test) { - char* s = "123"; - uint32_t type = 0; - int64_t val = 0; + char* s = "123"; int32_t ret = toInteger(s, strlen(s), 10, &val); ASSERT_EQ(ret, 0); ASSERT_EQ(val, 123); - s = "9223372036854775807"; - ret = toInteger(s, strlen(s), 10, &val); - ASSERT_EQ(ret, 0); - ASSERT_EQ(val, 9223372036854775807); - - s = "9323372036854775807"; // out of range, > int64_max - ret = toInteger(s, strlen(s), 10, &val); - ASSERT_EQ(ret, -1); - - s = "-9323372036854775807"; - ret = toInteger(s, strlen(s), 10, &val); - ASSERT_EQ(ret, -1); - s = "-1"; ret = toInteger(s, strlen(s), 10, &val); ASSERT_EQ(ret, 0); ASSERT_EQ(val, -1); - s = "-9223372036854775807"; - ret = toInteger(s, strlen(s), 10, &val); - ASSERT_EQ(ret, 0); - ASSERT_EQ(val, -9223372036854775807); - s = "1000u"; ret = toInteger(s, strlen(s), 10, &val); ASSERT_EQ(ret, -1); @@ -163,13 +215,22 @@ TEST(testCase, toInteger_test) { ASSERT_EQ(ret, 0); ASSERT_EQ(val, 72); - // 18446744073709551615 UINT64_MAX - s = "18446744073709551615"; + s = "9223372036854775807"; ret = toInteger(s, strlen(s), 10, &val); ASSERT_EQ(ret, 0); - ASSERT_EQ(val, 18446744073709551615u); + ASSERT_EQ(val, 9223372036854775807); - s = "18446744073709551616"; + s = "-9223372036854775808"; + ret = toInteger(s, strlen(s), 10, &val); + ASSERT_EQ(ret, 0); + ASSERT_EQ(val, -9223372036854775808); + + // out of range + s = "9323372036854775807"; + ret = toInteger(s, strlen(s), 10, &val); + ASSERT_EQ(ret, -1); + + s = "-9323372036854775807"; ret = toInteger(s, strlen(s), 10, &val); ASSERT_EQ(ret, -1); } @@ -225,7 +286,7 @@ TEST(testCase, Datablock_test) { printf("binary column length:%d\n", *(int32_t*)p1->pData); - ASSERT_EQ(blockDataGetNumOfCols(b), 3); + ASSERT_EQ(blockDataGetNumOfCols(b), 2); ASSERT_EQ(blockDataGetNumOfRows(b), 40); char* pData = colDataGetData(p1, 3); diff --git a/source/libs/parser/src/parInsertSql.c b/source/libs/parser/src/parInsertSql.c index c993efb9ae..dd62d05440 100644 --- a/source/libs/parser/src/parInsertSql.c +++ b/source/libs/parser/src/parInsertSql.c @@ -433,7 +433,7 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, } case TSDB_DATA_TYPE_TINYINT: { - code = toIntegerEx(pToken->z, pToken->n, &iv); + code = toIntegerEx(pToken->z, pToken->n, pToken->type, &iv); if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "invalid tinyint data", pToken->z); } else if (!IS_VALID_TINYINT(iv)) { @@ -445,7 +445,7 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, } case TSDB_DATA_TYPE_UTINYINT: { - code = toUIntegerEx(pToken->z, pToken->n, &uv); + code = toUIntegerEx(pToken->z, pToken->n, pToken->type, &uv); if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "invalid unsigned tinyint data", pToken->z); } else if (uv > UINT8_MAX) { @@ -456,7 +456,7 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, } case TSDB_DATA_TYPE_SMALLINT: { - code = toIntegerEx(pToken->z, pToken->n, &iv); + code = toIntegerEx(pToken->z, pToken->n, pToken->type, &iv); if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "invalid smallint data", pToken->z); } else if (!IS_VALID_SMALLINT(iv)) { @@ -467,7 +467,7 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, } case TSDB_DATA_TYPE_USMALLINT: { - code = toUIntegerEx(pToken->z, pToken->n, &uv); + code = toUIntegerEx(pToken->z, pToken->n, pToken->type, &uv); if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "invalid unsigned smallint data", pToken->z); } else if (uv > UINT16_MAX) { @@ -478,7 +478,7 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, } case TSDB_DATA_TYPE_INT: { - code = toIntegerEx(pToken->z, pToken->n, &iv); + code = toIntegerEx(pToken->z, pToken->n, pToken->type, &iv); if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "invalid int data", pToken->z); } else if (!IS_VALID_INT(iv)) { @@ -489,7 +489,7 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, } case TSDB_DATA_TYPE_UINT: { - code = toUIntegerEx(pToken->z, pToken->n, &uv); + code = toUIntegerEx(pToken->z, pToken->n, pToken->type, &uv); if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "invalid unsigned int data", pToken->z); } else if (uv > UINT32_MAX) { @@ -500,7 +500,7 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, } case TSDB_DATA_TYPE_BIGINT: { - code = toIntegerEx(pToken->z, pToken->n, &iv); + code = toIntegerEx(pToken->z, pToken->n, pToken->type, &iv); if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "invalid bigint data", pToken->z); } @@ -509,7 +509,7 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, } case TSDB_DATA_TYPE_UBIGINT: { - code = toUIntegerEx(pToken->z, pToken->n, &uv); + code = toUIntegerEx(pToken->z, pToken->n, pToken->type, &uv); if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "invalid unsigned bigint data", pToken->z); } @@ -519,11 +519,11 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, case TSDB_DATA_TYPE_FLOAT: { double dv; - if (TK_NK_ILLEGAL == toDouble(pToken, &dv, &endptr)) { + code = toDoubleEx(pToken->z, pToken->n, pToken->type, &dv); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(pMsgBuf, "illegal float data", pToken->z); } - if (((dv == HUGE_VAL || dv == -HUGE_VAL) && errno == ERANGE) || dv > FLT_MAX || dv < -FLT_MAX || isinf(dv) || - isnan(dv)) { + if (dv > FLT_MAX || dv < -FLT_MAX || isinf(dv) || isnan(dv)) { return buildSyntaxErrMsg(pMsgBuf, "illegal float data", pToken->z); } *(float*)(&val->i64) = dv; @@ -532,8 +532,9 @@ static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, case TSDB_DATA_TYPE_DOUBLE: { double dv; - if (TK_NK_ILLEGAL == toDouble(pToken, &dv, &endptr)) { - return buildSyntaxErrMsg(pMsgBuf, "illegal double data", pToken->z); + code = toDoubleEx(pToken->z, pToken->n, pToken->type, &dv); + if (TSDB_CODE_SUCCESS != code) { + return buildSyntaxErrMsg(pMsgBuf, "illegal float data", pToken->z); } if (((dv == HUGE_VAL || dv == -HUGE_VAL) && errno == ERANGE) || isinf(dv) || isnan(dv)) { return buildSyntaxErrMsg(pMsgBuf, "illegal double data", pToken->z); @@ -1359,7 +1360,7 @@ static int32_t parseValueTokenImpl(SInsertParseContext* pCxt, const char** pSql, break; } case TSDB_DATA_TYPE_TINYINT: { - int32_t code = toIntegerEx(pToken->z, pToken->n, &pVal->value.val); + int32_t code = toIntegerEx(pToken->z, pToken->n, pToken->type, &pVal->value.val); if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(&pCxt->msg, "invalid tinyint data", pToken->z); } else if (!IS_VALID_TINYINT(pVal->value.val)) { @@ -1368,7 +1369,7 @@ static int32_t parseValueTokenImpl(SInsertParseContext* pCxt, const char** pSql, break; } case TSDB_DATA_TYPE_UTINYINT: { - int32_t code = toUIntegerEx(pToken->z, pToken->n, &pVal->value.val); + int32_t code = toUIntegerEx(pToken->z, pToken->n, pToken->type, &pVal->value.val); if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(&pCxt->msg, "invalid unsigned tinyint data", pToken->z); } else if (pVal->value.val > UINT8_MAX) { @@ -1377,7 +1378,7 @@ static int32_t parseValueTokenImpl(SInsertParseContext* pCxt, const char** pSql, break; } case TSDB_DATA_TYPE_SMALLINT: { - int32_t code = toIntegerEx(pToken->z, pToken->n, &pVal->value.val); + int32_t code = toIntegerEx(pToken->z, pToken->n, pToken->type, &pVal->value.val); if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(&pCxt->msg, "invalid smallint data", pToken->z); } else if (!IS_VALID_SMALLINT(pVal->value.val)) { @@ -1386,7 +1387,7 @@ static int32_t parseValueTokenImpl(SInsertParseContext* pCxt, const char** pSql, break; } case TSDB_DATA_TYPE_USMALLINT: { - int32_t code = toUIntegerEx(pToken->z, pToken->n, &pVal->value.val); + int32_t code = toUIntegerEx(pToken->z, pToken->n, pToken->type, &pVal->value.val); if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(&pCxt->msg, "invalid unsigned smallint data", pToken->z); } else if (pVal->value.val > UINT16_MAX) { @@ -1395,7 +1396,7 @@ static int32_t parseValueTokenImpl(SInsertParseContext* pCxt, const char** pSql, break; } case TSDB_DATA_TYPE_INT: { - int32_t code = toIntegerEx(pToken->z, pToken->n, &pVal->value.val); + int32_t code = toIntegerEx(pToken->z, pToken->n, pToken->type, &pVal->value.val); if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(&pCxt->msg, "invalid int data", pToken->z); } else if (!IS_VALID_INT(pVal->value.val)) { @@ -1404,7 +1405,7 @@ static int32_t parseValueTokenImpl(SInsertParseContext* pCxt, const char** pSql, break; } case TSDB_DATA_TYPE_UINT: { - int32_t code = toUIntegerEx(pToken->z, pToken->n, &pVal->value.val); + int32_t code = toUIntegerEx(pToken->z, pToken->n, pToken->type, &pVal->value.val); if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(&pCxt->msg, "invalid unsigned int data", pToken->z); } else if (pVal->value.val > UINT32_MAX) { @@ -1413,27 +1414,26 @@ static int32_t parseValueTokenImpl(SInsertParseContext* pCxt, const char** pSql, break; } case TSDB_DATA_TYPE_BIGINT: { - int32_t code = toIntegerEx(pToken->z, pToken->n, &pVal->value.val); + int32_t code = toIntegerEx(pToken->z, pToken->n, pToken->type, &pVal->value.val); if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(&pCxt->msg, "invalid bigint data", pToken->z); } break; } case TSDB_DATA_TYPE_UBIGINT: { - int32_t code = toUIntegerEx(pToken->z, pToken->n, &pVal->value.val); + int32_t code = toUIntegerEx(pToken->z, pToken->n, pToken->type, &pVal->value.val); if (TSDB_CODE_SUCCESS != code) { - return buildSyntaxErrMsg(&pCxt->msg, "invalid unsigned int data", pToken->z); + return buildSyntaxErrMsg(&pCxt->msg, "invalid unsigned bigint data", pToken->z); } break; } case TSDB_DATA_TYPE_FLOAT: { - char* endptr = NULL; double dv; - if (TK_NK_ILLEGAL == toDouble(pToken, &dv, &endptr)) { + int32_t code = toDoubleEx(pToken->z, pToken->n, pToken->type, &dv); + if (TSDB_CODE_SUCCESS != code) { return buildSyntaxErrMsg(&pCxt->msg, "illegal float data", pToken->z); } - if (((dv == HUGE_VAL || dv == -HUGE_VAL) && errno == ERANGE) || dv > FLT_MAX || dv < -FLT_MAX || isinf(dv) || - isnan(dv)) { + if (dv > FLT_MAX || dv < -FLT_MAX || isinf(dv) || isnan(dv)) { return buildSyntaxErrMsg(&pCxt->msg, "illegal float data", pToken->z); } float f = dv; @@ -1441,12 +1441,12 @@ static int32_t parseValueTokenImpl(SInsertParseContext* pCxt, const char** pSql, break; } case TSDB_DATA_TYPE_DOUBLE: { - char* endptr = NULL; double dv; - if (TK_NK_ILLEGAL == toDouble(pToken, &dv, &endptr)) { - return buildSyntaxErrMsg(&pCxt->msg, "illegal double data", pToken->z); + int32_t code = toDoubleEx(pToken->z, pToken->n, pToken->type, &dv); + if (TSDB_CODE_SUCCESS != code) { + return buildSyntaxErrMsg(&pCxt->msg, "illegal float data", pToken->z); } - if (((dv == HUGE_VAL || dv == -HUGE_VAL) && errno == ERANGE) || isinf(dv) || isnan(dv)) { + if (isinf(dv) || isnan(dv)) { return buildSyntaxErrMsg(&pCxt->msg, "illegal double data", pToken->z); } pVal->value.val = *(int64_t*)&dv; diff --git a/source/libs/parser/src/parTokenizer.c b/source/libs/parser/src/parTokenizer.c index 5193bdc47f..5988c68e17 100644 --- a/source/libs/parser/src/parTokenizer.c +++ b/source/libs/parser/src/parTokenizer.c @@ -609,6 +609,11 @@ uint32_t tGetToken(const char* z, uint32_t* tokenId) { break; } + // support float with no decimal part after the decimal point + if (z[i] == '.' && seg == 1) { + *tokenId = TK_NK_FLOAT; + i++; + } if ((z[i] == 'e' || z[i] == 'E') && (isdigit(z[i + 1]) || ((z[i + 1] == '+' || z[i + 1] == '-') && isdigit(z[i + 2])))) { i += 2; @@ -751,7 +756,7 @@ SToken tStrGetToken(const char* str, int32_t* i, bool isPrevOptr, bool* pIgnoreC // support parse the -/+number format if ((isPrevOptr) && (t0.type == TK_NK_MINUS || t0.type == TK_NK_PLUS)) { len = tGetToken(&str[*i + t0.n], &type); - if (type == TK_NK_INTEGER || type == TK_NK_FLOAT) { + if (type == TK_NK_INTEGER || type == TK_NK_FLOAT || type == TK_NK_BIN || type == TK_NK_HEX) { t0.type = type; t0.n += len; } diff --git a/tests/parallel_test/cases.task b/tests/parallel_test/cases.task index 981a2411eb..15464eb1b3 100644 --- a/tests/parallel_test/cases.task +++ b/tests/parallel_test/cases.task @@ -249,6 +249,7 @@ e ,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/delete_check.py ,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/test_hot_refresh_configurations.py +,,y,system-test,./pytest.sh python3 ./test.py -f 1-insert/insert_double.py ,,y,system-test,./pytest.sh python3 ./test.py -f 1-insert/alter_database.py ,,y,system-test,./pytest.sh python3 ./test.py -f 1-insert/alter_replica.py -N 3 ,,y,system-test,./pytest.sh python3 ./test.py -f 1-insert/influxdb_line_taosc_insert.py diff --git a/tests/system-test/1-insert/insert_double.py b/tests/system-test/1-insert/insert_double.py new file mode 100644 index 0000000000..ce482287fb --- /dev/null +++ b/tests/system-test/1-insert/insert_double.py @@ -0,0 +1,133 @@ +import taos +import sys +import datetime +import inspect + +from util.log import * +from util.sql import * +from util.cases import * +import random + + +class TDTestCase: + + def init(self, conn, logSql, replicaVar=1): + self.replicaVar = int(replicaVar) + self.database = "db1" + tdLog.debug(f"start to excute {__file__}") + tdSql.init(conn.cursor(), False) + + def prepare_db(self): + tdSql.execute(f"drop database if exists {self.database}") + tdSql.execute(f"create database {self.database}") + tdSql.execute(f"use {self.database}") + + def test_value(self, table_name, dtype, bits): + tdSql.execute(f"drop table if exists {table_name}") + tdSql.execute(f"create table {table_name}(ts timestamp, i1 {dtype}, i2 {dtype} unsigned)") + + tdSql.execute(f"insert into {table_name} values(now, -16, +6)") + tdSql.execute(f"insert into {table_name} values(now, 80.99 , +0042 )") + tdSql.execute(f"insert into {table_name} values(now, -0042 , +80.99 )") + tdSql.execute(f"insert into {table_name} values(now, 52.34354, 18.6)") + tdSql.execute(f"insert into {table_name} values(now, -12., +3.)") + tdSql.execute(f"insert into {table_name} values(now, -.12, +.3)") + tdSql.execute(f"insert into {table_name} values(now, -2.3e1, +2.324e2)") + tdSql.execute(f"insert into {table_name} values(now, -2e1, +2e2)") + tdSql.execute(f"insert into {table_name} values(now, -2.e1, +2.e2)") + tdSql.execute(f"insert into {table_name} values(now, -0x40, +0b10000)") + tdSql.execute(f"insert into {table_name} values(now, -0b10000, +0x40)") + + # str support + tdSql.execute(f"insert into {table_name} values(now, '-16', '+6')") + tdSql.execute(f"insert into {table_name} values(now, ' -80.99 ', ' +0042 ')") + tdSql.execute(f"insert into {table_name} values(now, ' -0042 ', ' +80.99 ')") + tdSql.execute(f"insert into {table_name} values(now, '52.34354', '18.6')") + tdSql.execute(f"insert into {table_name} values(now, '-12.', '+5.')") + tdSql.execute(f"insert into {table_name} values(now, '-.12', '+.5')") + tdSql.execute(f"insert into {table_name} values(now, '-2.e1', '+2.e2')") + tdSql.execute(f"insert into {table_name} values(now, '-2e1', '+2e2')") + tdSql.execute(f"insert into {table_name} values(now, '-2.3e1', '+2.324e2')") + tdSql.execute(f"insert into {table_name} values(now, '-0x40', '+0b10010')") + tdSql.execute(f"insert into {table_name} values(now, '-0b10010', '+0x40')") + + tdSql.query(f"select * from {table_name}") + tdSql.checkRows(22) + + baseval = 2**(bits/2) + negval = -baseval + 1.645 + posval = baseval + 4.323 + bigval = 2**(bits-1) + max_i = bigval - 1 + min_i = -bigval + max_u = 2*bigval - 1 + min_u = 0 + print("val:", baseval, negval, posval, max_i) + + tdSql.execute(f"insert into {table_name} values(now, {negval}, {posval})") + tdSql.execute(f"insert into {table_name} values(now, -{baseval}, {baseval})") + tdSql.execute(f"insert into {table_name} values(now, {max_i}, {max_u})") + tdSql.execute(f"insert into {table_name} values(now, {min_i}, {min_u})") + + tdSql.query(f"select * from {table_name}") + tdSql.checkRows(26) + + # fail + tdSql.error(f"insert into {table_name} values(now, 0, {max_u+1})", "error") + tdSql.error(f"insert into {table_name} values(now, 0, {max_u+1})", "error") + tdSql.error(f"insert into {table_name} values(now, {max_i+1}, -1)", "error") + + + def test_tags(self, stable_name, dtype, bits): + tdSql.execute(f"create stable {stable_name}(ts timestamp, i1 {dtype}, i2 {dtype} unsigned) tags(id {dtype})") + + baseval = 2**(bits/2) + negval = -baseval + 1.645 + posval = baseval + 4.323 + bigval = 2**(bits-1) + max_i = bigval - 1 + min_i = -bigval + max_u = 2*bigval - 1 + min_u = 0 + + tdSql.execute(f"insert into {stable_name}_1 using {stable_name} tags('{negval}') values(now, {negval}, {posval})") + tdSql.execute(f"insert into {stable_name}_2 using {stable_name} tags({posval}) values(now, -{baseval} , {baseval})") + tdSql.execute(f"insert into {stable_name}_3 using {stable_name} tags('0x40') values(now, {max_i}, {max_u})") + tdSql.execute(f"insert into {stable_name}_4 using {stable_name} tags(0b10000) values(now, {min_i}, {min_u})") + + tdSql.execute(f"insert into {stable_name}_5 using {stable_name} tags({max_i}) values(now, '{negval}', '{posval}')") + tdSql.execute(f"insert into {stable_name}_6 using {stable_name} tags('{min_i}') values(now, '-{baseval}' , '{baseval}')") + tdSql.execute(f"insert into {stable_name}_7 using {stable_name} tags(-0x40) values(now, '{max_i}', '{max_u}')") + tdSql.execute(f"insert into {stable_name}_8 using {stable_name} tags('-0b10000') values(now, '{min_i}', '{min_u}')") + + tdSql.execute(f"insert into {stable_name}_9 using {stable_name} tags(12.) values(now, {negval}, {posval})") + tdSql.execute(f"insert into {stable_name}_10 using {stable_name} tags('-8.3') values(now, -{baseval} , {baseval})") + tdSql.execute(f"insert into {stable_name}_11 using {stable_name} tags(2.e1) values(now, {max_i}, {max_u})") + tdSql.execute(f"insert into {stable_name}_12 using {stable_name} tags('-2.3e1') values(now, {min_i}, {min_u})") + + tdSql.query(f"select * from {stable_name}") + tdSql.checkRows(12) + + def run(self): # sourcery skip: extract-duplicate-method, remove-redundant-fstring + tdSql.prepare(replica = self.replicaVar) + self.prepare_db() + + self.test_value("t1", "bigint", 64) + self.test_value("t2", "int", 32) + self.test_value("t3", "smallint", 16) + self.test_value("t4", "tinyint", 8) + tdLog.printNoPrefix("==========end case1 run ...............") + + self.test_tags("t_big", "bigint", 64) + self.test_tags("t_int", "int", 32) + self.test_tags("t_small", "smallint", 16) + self.test_tags("t_tiny", "tinyint", 8) + tdLog.printNoPrefix("==========end case2 run ...............") + + def stop(self): + tdSql.close() + tdLog.success(f"{__file__} successfully executed") + + +tdCases.addLinux(__file__, TDTestCase()) +tdCases.addWindows(__file__, TDTestCase()) From 9fa884a693ab876cd0c8949870baa5f82cfd6bad Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Wed, 6 Dec 2023 01:24:23 +0800 Subject: [PATCH 03/10] toUIntegerEx --- source/common/src/tvariant.c | 122 ++++++++++---------- source/common/test/commonTests.cpp | 2 +- tests/system-test/1-insert/insert_double.py | 4 +- 3 files changed, 67 insertions(+), 61 deletions(-) diff --git a/source/common/src/tvariant.c b/source/common/src/tvariant.c index 609bcf1382..25f025ad3d 100644 --- a/source/common/src/tvariant.c +++ b/source/common/src/tvariant.c @@ -45,18 +45,7 @@ int32_t parseBinaryUInteger(const char *z, int32_t n, uint64_t *value) { return TSDB_CODE_SUCCESS; } -int32_t parseHexUInteger(const char *z, int32_t n, uint64_t *value) { - errno = 0; - char *endPtr = NULL; - *value = taosStr2UInt64(z, &endPtr, 16); - if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { - return TSDB_CODE_FAILED; - } - return TSDB_CODE_SUCCESS; -} - int32_t parseSignAndUInteger(const char *z, int32_t n, bool *is_neg, uint64_t *value) { - // parse sign bool has_sign = false; if (z[0] == '-') { @@ -75,6 +64,9 @@ int32_t parseSignAndUInteger(const char *z, int32_t n, bool *is_neg, uint64_t *v n--; } + errno = 0; + char *endPtr = NULL; + bool parsed = false; if (z[0] == '0' && n > 2) { if (z[1] == 'b' || z[1] == 'B') { // paring as binary @@ -83,18 +75,35 @@ int32_t parseSignAndUInteger(const char *z, int32_t n, bool *is_neg, uint64_t *v if (z[1] == 'x' || z[1] == 'X') { // parsing as hex - return parseHexUInteger(z, n, value); + *value = taosStr2UInt64(z, &endPtr, 16); + parsed = true; } } - // parsing as double - errno = 0; - char *endPtr = NULL; - double val = taosStr2Double(z, &endPtr); - if (errno == ERANGE || errno == EINVAL || endPtr - z != n || val > UINT64_MAX) { + if (!parsed) { + bool parse_int = true; + for (int32_t i = 0; i < n; i++) { + if (z[i] < '0' || z[i] > '9') { + parse_int = false; + break; + } + } + if (parse_int) { + // parsing as decimal + *value = taosStr2UInt64(z, &endPtr, 10); + } else { + // parsing as double + double val = taosStr2Double(z, &endPtr); + if (val > UINT64_MAX) { + return TSDB_CODE_FAILED; + } + *value = round(val); + } + } + + if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { return TSDB_CODE_FAILED; } - *value = round(val); return TSDB_CODE_SUCCESS; } @@ -113,15 +122,13 @@ int32_t removeSpace(const char **pp, int32_t n) { } int32_t toDoubleEx(const char *z, int32_t n, uint32_t type, double* value) { - if (type != TK_NK_FLOAT) { - if (n == 0) { - *value = 0; - return TSDB_CODE_SUCCESS; - } - // rm tail space - while (n > 1 && z[n-1] == ' ') { - n--; - } + if (n == 0) { + *value = 0; + return TSDB_CODE_SUCCESS; + } + // rm tail space + while (n > 1 && z[n-1] == ' ') { + n--; } errno = 0; @@ -137,33 +144,38 @@ int32_t toDoubleEx(const char *z, int32_t n, uint32_t type, double* value) { int32_t toIntegerEx(const char *z, int32_t n, uint32_t type, int64_t *value) { errno = 0; char *endPtr = NULL; + bool parsed = false; switch (type) { case TK_NK_INTEGER: { *value = taosStr2Int64(z, &endPtr, 10); - if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { + parsed = true; + } break; + case TK_NK_FLOAT: { + double val = taosStr2Double(z, &endPtr); + parsed = true; + if (!IS_VALID_INT64(val)) { return TSDB_CODE_FAILED; } - return TSDB_CODE_SUCCESS; - } - case TK_NK_HEX: { - *value = taosStr2Int64(z, &endPtr, 16); - if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { - return TSDB_CODE_FAILED; - } - return TSDB_CODE_SUCCESS; - } - case TK_NK_BIN: { - *value = taosStr2Int64(z, &endPtr, 2); - if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { - return TSDB_CODE_FAILED; - } - return TSDB_CODE_SUCCESS; - } + *value = round(val); + } break; default: break; } + if (parsed) { + if (errno == ERANGE || errno == EINVAL) { + return TSDB_CODE_FAILED; + } + while (n > 0 && z[n-1] == ' ') { + n--; + } + if (endPtr - z != n) { + return TSDB_CODE_FAILED; + } + return TSDB_CODE_SUCCESS; + } + // parse string if (n == 0) { *value = 0; @@ -196,29 +208,23 @@ int32_t toIntegerEx(const char *z, int32_t n, uint32_t type, int64_t *value) { } int32_t toUIntegerEx(const char *z, int32_t n, uint32_t type, uint64_t *value) { - errno = 0; - char *endPtr = NULL; + switch (type) { case TK_NK_INTEGER: { - *value = taosStr2UInt64(z, &endPtr, 10); - if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { - return TSDB_CODE_FAILED; - } - return TSDB_CODE_SUCCESS; + return toUInteger(z, n, 10, value); } - case TK_NK_HEX: { - *value = taosStr2UInt64(z, &endPtr, 16); + case TK_NK_FLOAT: { + errno = 0; + char *endPtr = NULL; + double val = round(taosStr2Double(z, &endPtr)); if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { return TSDB_CODE_FAILED; } - return TSDB_CODE_SUCCESS; - } - case TK_NK_BIN: { - *value = taosStr2UInt64(z, &endPtr, 2); - if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { + if (!IS_VALID_UINT64(val)) { return TSDB_CODE_FAILED; } + *value = val; return TSDB_CODE_SUCCESS; } default: diff --git a/source/common/test/commonTests.cpp b/source/common/test/commonTests.cpp index 291941d9b2..9f7ee165ac 100644 --- a/source/common/test/commonTests.cpp +++ b/source/common/test/commonTests.cpp @@ -128,7 +128,7 @@ TEST(testCase, toIntegerEx_test) { ASSERT_EQ(val, -64); s = "0b110"; - ret = toIntegerEx(s, strlen(s), 0, &val); + ret = toIntegerEx(s, strlen(s), TK_NK_BIN, &val); ASSERT_EQ(ret, 0); ASSERT_EQ(val, 6); diff --git a/tests/system-test/1-insert/insert_double.py b/tests/system-test/1-insert/insert_double.py index ce482287fb..8a7180325b 100644 --- a/tests/system-test/1-insert/insert_double.py +++ b/tests/system-test/1-insert/insert_double.py @@ -27,8 +27,8 @@ class TDTestCase: tdSql.execute(f"create table {table_name}(ts timestamp, i1 {dtype}, i2 {dtype} unsigned)") tdSql.execute(f"insert into {table_name} values(now, -16, +6)") - tdSql.execute(f"insert into {table_name} values(now, 80.99 , +0042 )") - tdSql.execute(f"insert into {table_name} values(now, -0042 , +80.99 )") + tdSql.execute(f"insert into {table_name} values(now, 80.99, +0042)") + tdSql.execute(f"insert into {table_name} values(now, -0042, +80.99)") tdSql.execute(f"insert into {table_name} values(now, 52.34354, 18.6)") tdSql.execute(f"insert into {table_name} values(now, -12., +3.)") tdSql.execute(f"insert into {table_name} values(now, -.12, +.3)") From 17866b2b855190dc016f4d316a3dd9376d292421 Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Wed, 6 Dec 2023 02:44:35 +0800 Subject: [PATCH 04/10] case adjust --- tests/system-test/1-insert/insert_double.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system-test/1-insert/insert_double.py b/tests/system-test/1-insert/insert_double.py index 8a7180325b..54df7e0352 100644 --- a/tests/system-test/1-insert/insert_double.py +++ b/tests/system-test/1-insert/insert_double.py @@ -31,7 +31,7 @@ class TDTestCase: tdSql.execute(f"insert into {table_name} values(now, -0042, +80.99)") tdSql.execute(f"insert into {table_name} values(now, 52.34354, 18.6)") tdSql.execute(f"insert into {table_name} values(now, -12., +3.)") - tdSql.execute(f"insert into {table_name} values(now, -.12, +.3)") + tdSql.execute(f"insert into {table_name} values(now, -0.12, +3.0)") tdSql.execute(f"insert into {table_name} values(now, -2.3e1, +2.324e2)") tdSql.execute(f"insert into {table_name} values(now, -2e1, +2e2)") tdSql.execute(f"insert into {table_name} values(now, -2.e1, +2.e2)") From 1ec8bf9ef959782d6976548fc9d41013a5e38bf6 Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Wed, 6 Dec 2023 10:11:52 +0800 Subject: [PATCH 05/10] add error case --- source/common/src/tvariant.c | 23 ++++++++++++--------- tests/system-test/1-insert/insert_double.py | 13 +++++++----- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/source/common/src/tvariant.c b/source/common/src/tvariant.c index 25f025ad3d..d78c76a543 100644 --- a/source/common/src/tvariant.c +++ b/source/common/src/tvariant.c @@ -126,16 +126,19 @@ int32_t toDoubleEx(const char *z, int32_t n, uint32_t type, double* value) { *value = 0; return TSDB_CODE_SUCCESS; } - // rm tail space - while (n > 1 && z[n-1] == ' ') { - n--; - } errno = 0; char* endPtr = NULL; *value = taosStr2Double(z, &endPtr); - if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { + if (errno == ERANGE || errno == EINVAL) { + return TSDB_CODE_FAILED; + } + // rm tail space + while (n > 1 && z[n-1] == ' ') { + n--; + } + if (endPtr - z != n) { return TSDB_CODE_FAILED; } return TSDB_CODE_SUCCESS; @@ -152,12 +155,12 @@ int32_t toIntegerEx(const char *z, int32_t n, uint32_t type, int64_t *value) { parsed = true; } break; case TK_NK_FLOAT: { - double val = taosStr2Double(z, &endPtr); + double val = round(taosStr2Double(z, &endPtr)); parsed = true; if (!IS_VALID_INT64(val)) { return TSDB_CODE_FAILED; } - *value = round(val); + *value = val; } break; default: break; @@ -167,7 +170,7 @@ int32_t toIntegerEx(const char *z, int32_t n, uint32_t type, int64_t *value) { if (errno == ERANGE || errno == EINVAL) { return TSDB_CODE_FAILED; } - while (n > 0 && z[n-1] == ' ') { + while (n > 1 && z[n-1] == ' ') { n--; } if (endPtr - z != n) { @@ -176,7 +179,7 @@ int32_t toIntegerEx(const char *z, int32_t n, uint32_t type, int64_t *value) { return TSDB_CODE_SUCCESS; } - // parse string + // parse as string if (n == 0) { *value = 0; return TSDB_CODE_SUCCESS; @@ -231,7 +234,7 @@ int32_t toUIntegerEx(const char *z, int32_t n, uint32_t type, uint64_t *value) { break; } - // parse string + // parse as string if (n == 0) { *value = 0; return TSDB_CODE_SUCCESS; diff --git a/tests/system-test/1-insert/insert_double.py b/tests/system-test/1-insert/insert_double.py index 54df7e0352..56861f3cd4 100644 --- a/tests/system-test/1-insert/insert_double.py +++ b/tests/system-test/1-insert/insert_double.py @@ -72,11 +72,14 @@ class TDTestCase: tdSql.query(f"select * from {table_name}") tdSql.checkRows(26) - # fail - tdSql.error(f"insert into {table_name} values(now, 0, {max_u+1})", "error") - tdSql.error(f"insert into {table_name} values(now, 0, {max_u+1})", "error") - tdSql.error(f"insert into {table_name} values(now, {max_i+1}, -1)", "error") - + # error case + tdSql.error(f"insert into {table_name} values(now, 0, {max_u+1})") + tdSql.error(f"insert into {table_name} values(now, 0, -1)") + tdSql.error(f"insert into {table_name} values(now, 0, -2.0)") + tdSql.error(f"insert into {table_name} values(now, 0, '-2.0')") + tdSql.error(f"insert into {table_name} values(now, {max_i+1}, 0)") + tdSql.error(f"insert into {table_name} values(now, {min_i-1}, 0)") + tdSql.error(f"insert into {table_name} values(now, '{min_i-1}', 0)") def test_tags(self, stable_name, dtype, bits): tdSql.execute(f"create stable {stable_name}(ts timestamp, i1 {dtype}, i2 {dtype} unsigned) tags(id {dtype})") From bfc12f03cb3e50018b35df3caab0527d5e079d71 Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Wed, 6 Dec 2023 21:26:29 +0800 Subject: [PATCH 06/10] optimize & perf test --- source/common/src/tvariant.c | 126 ++++++++++++++-------- tests/system-test/1-insert/insert_perf.py | 118 ++++++++++++++++++++ 2 files changed, 198 insertions(+), 46 deletions(-) create mode 100644 tests/system-test/1-insert/insert_perf.py diff --git a/source/common/src/tvariant.c b/source/common/src/tvariant.c index d78c76a543..384df73342 100644 --- a/source/common/src/tvariant.c +++ b/source/common/src/tvariant.c @@ -81,24 +81,12 @@ int32_t parseSignAndUInteger(const char *z, int32_t n, bool *is_neg, uint64_t *v } if (!parsed) { - bool parse_int = true; - for (int32_t i = 0; i < n; i++) { - if (z[i] < '0' || z[i] > '9') { - parse_int = false; - break; - } - } - if (parse_int) { - // parsing as decimal - *value = taosStr2UInt64(z, &endPtr, 10); - } else { - // parsing as double - double val = taosStr2Double(z, &endPtr); - if (val > UINT64_MAX) { - return TSDB_CODE_FAILED; - } - *value = round(val); + // parsing as double + double val = taosStr2Double(z, &endPtr); + if (val > UINT64_MAX) { + return TSDB_CODE_FAILED; } + *value = round(val); } if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { @@ -145,6 +133,11 @@ int32_t toDoubleEx(const char *z, int32_t n, uint32_t type, double* value) { } int32_t toIntegerEx(const char *z, int32_t n, uint32_t type, int64_t *value) { + if (n == 0) { + *value = 0; + return TSDB_CODE_SUCCESS; + } + errno = 0; char *endPtr = NULL; bool parsed = false; @@ -179,13 +172,21 @@ int32_t toIntegerEx(const char *z, int32_t n, uint32_t type, int64_t *value) { return TSDB_CODE_SUCCESS; } - // parse as string - if (n == 0) { - *value = 0; + // rm tail space + while (n > 1 && z[n-1] == ' ') { + n--; + } + + // 1. try to parse as integer + *value = taosStr2Int64(z, &endPtr, 10); + if (endPtr - z == n) { + if (errno == ERANGE || errno == EINVAL) { + return TSDB_CODE_FAILED; + } return TSDB_CODE_SUCCESS; } - n = removeSpace(&z, n); + // 2. parse as other bool is_neg = false; uint64_t uv = 0; int32_t code = parseSignAndUInteger(z, n, &is_neg, &uv); @@ -211,36 +212,69 @@ int32_t toIntegerEx(const char *z, int32_t n, uint32_t type, int64_t *value) { } int32_t toUIntegerEx(const char *z, int32_t n, uint32_t type, uint64_t *value) { - - switch (type) - { - case TK_NK_INTEGER: { - return toUInteger(z, n, 10, value); - } - case TK_NK_FLOAT: { - errno = 0; - char *endPtr = NULL; - double val = round(taosStr2Double(z, &endPtr)); - if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { - return TSDB_CODE_FAILED; - } - if (!IS_VALID_UINT64(val)) { - return TSDB_CODE_FAILED; - } - *value = val; - return TSDB_CODE_SUCCESS; - } - default: - break; - } - - // parse as string if (n == 0) { *value = 0; return TSDB_CODE_SUCCESS; } - n = removeSpace(&z, n); + errno = 0; + const char *p = z; + char *endPtr = NULL; + bool parsed = false; + while (*p == ' ') { + p++; + } + switch (type) { + case TK_NK_INTEGER: { + *value = taosStr2UInt64(p, &endPtr, 10); + if (*p == '-' && *value) { + return TSDB_CODE_FAILED; + } + parsed = true; + } break; + case TK_NK_FLOAT: { + double val = round(taosStr2Double(p, &endPtr)); + if (!IS_VALID_UINT64(val)) { + return TSDB_CODE_FAILED; + } + *value = val; + parsed = true; + } break; + default: + break; + } + + if (parsed) { + if (errno == ERANGE || errno == EINVAL) { + return TSDB_CODE_FAILED; + } + while (n > 1 && z[n-1] == ' ') { + n--; + } + if (endPtr - z != n) { + return TSDB_CODE_FAILED; + } + return TSDB_CODE_SUCCESS; + } + + // rm tail space + while (n > 1 && z[n-1] == ' ') { + n--; + } + + // 1. parse as integer + *value = taosStr2UInt64(p, &endPtr, 10); + if (endPtr - z == n) { + if (*p == '-' && *value) { + return TSDB_CODE_FAILED; + } + if (errno == ERANGE || errno == EINVAL) { + return TSDB_CODE_FAILED; + } + return TSDB_CODE_SUCCESS; + } + + // 2. parse as other bool is_neg = false; int32_t code = parseSignAndUInteger(z, n, &is_neg, value); if (is_neg) { diff --git a/tests/system-test/1-insert/insert_perf.py b/tests/system-test/1-insert/insert_perf.py new file mode 100644 index 0000000000..bf6fb6799d --- /dev/null +++ b/tests/system-test/1-insert/insert_perf.py @@ -0,0 +1,118 @@ +import taos +import sys +import random +import time +import csv + +from datetime import datetime + +from util.log import * +from util.sql import * +from util.cases import * + + + +class TDTestCase: + + def init(self, conn, logSql, replicaVar=1): + self.replicaVar = int(replicaVar) + + self.testcasePath = os.path.split(__file__)[0] + self.testcasefilename = os.path.split(__file__)[-1] + self.file1 = f"{self.testcasePath}/int.csv" + self.file2 = f"{self.testcasePath}/double.csv" + self.ts = 1700638570000 # 2023-11-22T07:36:10.000Z + self.database = "db1" + self.tb1 = "t1" + self.tb2 = "t2" + self.stable1 = "st1" + self.stable2 = "st2" + self.tag1 = f'using {self.stable1}(groupId) tags(1)' + self.tag2 = f'using {self.stable2}(groupId) tags(2)' + self.once = 1000 + tdLog.debug(f"start to excute {__file__}") + tdSql.init(conn.cursor(), False) + + def prepare_db(self): + #tdSql.execute(f"drop database if exists {self.database}") + tdSql.execute(f"create database if not exists {self.database}") + tdSql.execute(f"use {self.database}") + tdSql.execute(f"create stable if not exists {self.stable1} (ts timestamp, q_bigint bigint , i1 int, i2 int, i3 int, i4 int, i5 int, i6 int, i7 int, i8 int, i9 int) tags (location binary(64), groupId int);") + tdSql.execute(f"create stable if not exists {self.stable2} (ts timestamp, q_double double, f1 double, f2 double, f3 double, f4 double, f5 double, f6 double, f7 double, f8 double, f9 double) tags (location binary(64), groupId int);") + tdSql.execute(f"drop table if exists {self.tb1};") + tdSql.execute(f"drop table if exists {self.tb2};") + tdSql.execute(f"create table {self.tb1} {self.tag1};") + tdSql.execute(f"create table {self.tb2} {self.tag2};") + + def make_csv(self, filepath, once, isint): + f = open(filepath, 'w') + with f: + writer = csv.writer(f) + rows = [] + for i in range(once): + r = [] + if isint: + for k in range(10): + r.append(random.randint(-2147483648, 2147483647)) + else: + for k in range(10): + r.append(random.randint(-2147483648, 2147483647) + random.random()) + rows.append(r) + writer.writerows(rows) + f.close() + print(f"{filepath} ready!") + + def test_insert(self, tbname, qtime, startts, filepath, isint): + f = open(filepath, 'r') + rows = [] + with f: + reader = csv.reader(f, delimiter=',', quotechar='|') + for row in reader: + rows.append(row) + f.close() + self.once = len(rows) + + sum = 0 + for j in range(qtime): + offset = j * self.once + ts = startts + offset + sql = f"insert into {self.database}.{tbname} values" + for i in range(self.once): + r = rows[i] + sql +=f"({ts + i},'{r[0]}','{r[1]}','{r[2]}','{r[3]}','{r[4]}','{r[5]}','{r[6]}','{r[7]}','{r[8]}','{r[9]}')" + + t1 = time.time() + tdSql.execute(f"{sql};", 1) + t2 = time.time() + #print(f"{t2} insert test {j}.") + #print(sql) + sum += t2 - t1 + + sum = sum + tbtype = "10 double col/per row" + if isint: + tbtype = "10 int col/per row" + print(f" insert {self.once} * {qtime} rows: {sum} s, {tbtype}") + + # tdSql.query(f"select count(*) from {self.database}.{tbname};") + # tdSql.checkData(0, 0, once*qtime) + + def run(self): + tdSql.prepare(replica = self.replicaVar) + # self.make_csv(self.file1, self.once, True) + # self.make_csv(self.file2, self.once, False) + self.prepare_db() + self.test_insert(self.tb1, 10000, self.ts, self.file1, True) + self.test_insert(self.tb2, 10000, self.ts, self.file2, False) + self.test_insert(self.tb2, 10000, self.ts, self.file1, False) + + self.test_insert(self.tb1, 10000, self.ts, self.file2, True) + + + def stop(self): + tdSql.close() + tdLog.success(f"{__file__} successfully executed") + + +tdCases.addLinux(__file__, TDTestCase()) +tdCases.addWindows(__file__, TDTestCase()) From 8f5df0b6a3aba15831f8372051315f4005c582a1 Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Thu, 7 Dec 2023 01:51:23 +0800 Subject: [PATCH 07/10] opt double -> int --- source/common/src/tvariant.c | 68 +++++++++++++----- tests/system-test/1-insert/insert_perf.py | 86 +++++++++++++++-------- 2 files changed, 108 insertions(+), 46 deletions(-) diff --git a/source/common/src/tvariant.c b/source/common/src/tvariant.c index 384df73342..5c7567777f 100644 --- a/source/common/src/tvariant.c +++ b/source/common/src/tvariant.c @@ -95,19 +95,6 @@ int32_t parseSignAndUInteger(const char *z, int32_t n, bool *is_neg, uint64_t *v return TSDB_CODE_SUCCESS; } -int32_t removeSpace(const char **pp, int32_t n) { - // rm blank space from both head and tail, keep at least one char - const char *z = *pp; - while (n > 1 && *z == ' ') { - z++; - n--; - } - while (n > 1 && z[n-1] == ' ') { - n--; - } - *pp = z; - return n; -} int32_t toDoubleEx(const char *z, int32_t n, uint32_t type, double* value) { if (n == 0) { @@ -184,6 +171,36 @@ int32_t toIntegerEx(const char *z, int32_t n, uint32_t type, int64_t *value) { return TSDB_CODE_FAILED; } return TSDB_CODE_SUCCESS; + } else if (errno == 0 && *endPtr == '.') { + // pure decimal part + const char *s = endPtr + 1; + const char *end = z + n; + bool pure = true; + while (s < end) { + if (*s < '0' || *s > '9') { + pure = false; + break; + } + s++; + } + if (pure) { + if (endPtr+1 < end && endPtr[1] > '4') { + const char *p = z; + while (*p == ' ') { + p++; + } + if (*p == '-') { + if ( *value > INT64_MIN) { + (*value)--; + } + } else { + if ( *value < INT64_MAX) { + (*value)++; + } + } + } + return TSDB_CODE_SUCCESS; + } } // 2. parse as other @@ -218,9 +235,9 @@ int32_t toUIntegerEx(const char *z, int32_t n, uint32_t type, uint64_t *value) { } errno = 0; - const char *p = z; char *endPtr = NULL; bool parsed = false; + const char *p = z; while (*p == ' ') { p++; } @@ -264,14 +281,31 @@ int32_t toUIntegerEx(const char *z, int32_t n, uint32_t type, uint64_t *value) { // 1. parse as integer *value = taosStr2UInt64(p, &endPtr, 10); + if (*p == '-' && *value) { + return TSDB_CODE_FAILED; + } if (endPtr - z == n) { - if (*p == '-' && *value) { - return TSDB_CODE_FAILED; - } if (errno == ERANGE || errno == EINVAL) { return TSDB_CODE_FAILED; } return TSDB_CODE_SUCCESS; + } else if (errno == 0 && *endPtr == '.') { + const char *s = endPtr + 1; + const char *end = z + n; + bool pure = true; + while (s < end) { + if (*s < '0' || *s > '9') { + pure = false; + break; + } + s++; + } + if (pure) { + if (endPtr + 1 < end && endPtr[1] > '4' && *value < UINT64_MAX) { + (*value)++; + } + return TSDB_CODE_SUCCESS; + } } // 2. parse as other diff --git a/tests/system-test/1-insert/insert_perf.py b/tests/system-test/1-insert/insert_perf.py index bf6fb6799d..d96a031458 100644 --- a/tests/system-test/1-insert/insert_perf.py +++ b/tests/system-test/1-insert/insert_perf.py @@ -21,48 +21,70 @@ class TDTestCase: self.testcasefilename = os.path.split(__file__)[-1] self.file1 = f"{self.testcasePath}/int.csv" self.file2 = f"{self.testcasePath}/double.csv" + self.file3 = f"{self.testcasePath}/d+.csv" + self.file4 = f"{self.testcasePath}/uint.csv" self.ts = 1700638570000 # 2023-11-22T07:36:10.000Z self.database = "db1" self.tb1 = "t1" self.tb2 = "t2" - self.stable1 = "st1" - self.stable2 = "st2" - self.tag1 = f'using {self.stable1}(groupId) tags(1)' - self.tag2 = f'using {self.stable2}(groupId) tags(2)' + self.tb3 = "t3" self.once = 1000 tdLog.debug(f"start to excute {__file__}") tdSql.init(conn.cursor(), False) def prepare_db(self): - #tdSql.execute(f"drop database if exists {self.database}") - tdSql.execute(f"create database if not exists {self.database}") + tdSql.execute(f"drop database if exists {self.database}") + tdSql.execute(f"create database {self.database}") tdSql.execute(f"use {self.database}") - tdSql.execute(f"create stable if not exists {self.stable1} (ts timestamp, q_bigint bigint , i1 int, i2 int, i3 int, i4 int, i5 int, i6 int, i7 int, i8 int, i9 int) tags (location binary(64), groupId int);") - tdSql.execute(f"create stable if not exists {self.stable2} (ts timestamp, q_double double, f1 double, f2 double, f3 double, f4 double, f5 double, f6 double, f7 double, f8 double, f9 double) tags (location binary(64), groupId int);") - tdSql.execute(f"drop table if exists {self.tb1};") - tdSql.execute(f"drop table if exists {self.tb2};") - tdSql.execute(f"create table {self.tb1} {self.tag1};") - tdSql.execute(f"create table {self.tb2} {self.tag2};") - - def make_csv(self, filepath, once, isint): + tdSql.execute(f"create table {self.tb1} (ts timestamp, i0 bigint , i1 bigint, i2 bigint, i3 bigint, i4 bigint, i5 bigint, i6 bigint, i7 bigint, i8 bigint, i9 bigint)") + tdSql.execute(f"create table {self.tb2} (ts timestamp, f0 double, f1 double, f2 double, f3 double, f4 double, f5 double, f6 double, f7 double, f8 double, f9 double)") + tdSql.execute(f"create table {self.tb3} (ts timestamp, i0 int unsigned , i1 int unsigned, i2 int unsigned, i3 int unsigned, i4 int unsigned, i5 int unsigned, i6 int unsigned, i7 int unsigned, i8 int unsigned, i9 int unsigned)") + + def make_csv(self, once, intype): + filepath = self.file1 + if intype == 2: + filepath = self.file2 + elif intype == 3: + filepath = self.file3 + elif intype == 4: + filepath = self.file4 + f = open(filepath, 'w') with f: writer = csv.writer(f) rows = [] for i in range(once): r = [] - if isint: + if intype == 1: for k in range(10): r.append(random.randint(-2147483648, 2147483647)) + elif intype == 2: + for k in range(10): + r.append(random.randint(-2147483648, 2147483646) + random.random()) + elif intype == 3: + for k in range(10): + r.append(random.randint(0, 4294967294) + random.random()) else: for k in range(10): - r.append(random.randint(-2147483648, 2147483647) + random.random()) + r.append(random.randint(0, 4294967295)) rows.append(r) writer.writerows(rows) f.close() print(f"{filepath} ready!") - def test_insert(self, tbname, qtime, startts, filepath, isint): + def test_insert(self, tbname, qtime, startts, intype, outtype): + filepath = self.file1 + dinfo = "int" + if intype == 2: + filepath = self.file2 + dinfo = "double" + elif intype == 3: + filepath = self.file3 + dinfo = "+double" + elif intype == 4: + filepath = self.file4 + dinfo = "uint" + f = open(filepath, 'r') rows = [] with f: @@ -89,25 +111,31 @@ class TDTestCase: sum += t2 - t1 sum = sum - tbtype = "10 double col/per row" - if isint: - tbtype = "10 int col/per row" - print(f" insert {self.once} * {qtime} rows: {sum} s, {tbtype}") + tbinfo = "10 bigint col/per row" + if outtype == 2: + tbinfo = "10 double col/per row" + elif outtype == 3: + tbinfo = "10 uint col/per row" + print(f" insert {self.once} * {qtime} rows: {sum} s, {dinfo} -> {tbinfo}") # tdSql.query(f"select count(*) from {self.database}.{tbname};") # tdSql.checkData(0, 0, once*qtime) def run(self): tdSql.prepare(replica = self.replicaVar) - # self.make_csv(self.file1, self.once, True) - # self.make_csv(self.file2, self.once, False) + # self.make_csv(self.once, 1) + # self.make_csv(self.once, 2) + # self.make_csv(self.once, 3) + # self.make_csv(self.once, 4) + self.prepare_db() - self.test_insert(self.tb1, 10000, self.ts, self.file1, True) - self.test_insert(self.tb2, 10000, self.ts, self.file2, False) - self.test_insert(self.tb2, 10000, self.ts, self.file1, False) - - self.test_insert(self.tb1, 10000, self.ts, self.file2, True) - + self.test_insert(self.tb1, 1000, self.ts-10000000, 1, 1) + self.test_insert(self.tb2, 1000, self.ts-10000000, 2, 2) + self.test_insert(self.tb3, 1000, self.ts-10000000, 4, 3) + self.test_insert(self.tb2, 1000, self.ts, 1, 2) + + self.test_insert(self.tb1, 1000, self.ts, 2, 1) + self.test_insert(self.tb3, 1000, self.ts, 3, 3) def stop(self): tdSql.close() From 07af4e9a08e80935bcdefd5a36ac36bcaf62ebf0 Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Thu, 7 Dec 2023 12:02:04 +0800 Subject: [PATCH 08/10] adjust --- source/common/src/tvariant.c | 56 +++++++++++++----------------------- 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/source/common/src/tvariant.c b/source/common/src/tvariant.c index 5c7567777f..40de0428ff 100644 --- a/source/common/src/tvariant.c +++ b/source/common/src/tvariant.c @@ -120,42 +120,34 @@ int32_t toDoubleEx(const char *z, int32_t n, uint32_t type, double* value) { } int32_t toIntegerEx(const char *z, int32_t n, uint32_t type, int64_t *value) { - if (n == 0) { - *value = 0; - return TSDB_CODE_SUCCESS; - } - errno = 0; char *endPtr = NULL; - bool parsed = false; switch (type) { case TK_NK_INTEGER: { *value = taosStr2Int64(z, &endPtr, 10); - parsed = true; + if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { + return TSDB_CODE_FAILED; + } + return TSDB_CODE_SUCCESS; } break; case TK_NK_FLOAT: { double val = round(taosStr2Double(z, &endPtr)); - parsed = true; if (!IS_VALID_INT64(val)) { return TSDB_CODE_FAILED; } + if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { + return TSDB_CODE_FAILED; + } *value = val; + return TSDB_CODE_SUCCESS; } break; default: break; } - if (parsed) { - if (errno == ERANGE || errno == EINVAL) { - return TSDB_CODE_FAILED; - } - while (n > 1 && z[n-1] == ' ') { - n--; - } - if (endPtr - z != n) { - return TSDB_CODE_FAILED; - } + if (n == 0) { + *value = 0; return TSDB_CODE_SUCCESS; } @@ -229,14 +221,8 @@ int32_t toIntegerEx(const char *z, int32_t n, uint32_t type, int64_t *value) { } int32_t toUIntegerEx(const char *z, int32_t n, uint32_t type, uint64_t *value) { - if (n == 0) { - *value = 0; - return TSDB_CODE_SUCCESS; - } - errno = 0; char *endPtr = NULL; - bool parsed = false; const char *p = z; while (*p == ' ') { p++; @@ -247,30 +233,28 @@ int32_t toUIntegerEx(const char *z, int32_t n, uint32_t type, uint64_t *value) { if (*p == '-' && *value) { return TSDB_CODE_FAILED; } - parsed = true; + if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { + return TSDB_CODE_FAILED; + } + return TSDB_CODE_SUCCESS; } break; case TK_NK_FLOAT: { double val = round(taosStr2Double(p, &endPtr)); if (!IS_VALID_UINT64(val)) { return TSDB_CODE_FAILED; } + if (errno == ERANGE || errno == EINVAL || endPtr - z != n) { + return TSDB_CODE_FAILED; + } *value = val; - parsed = true; + return TSDB_CODE_SUCCESS; } break; default: break; } - if (parsed) { - if (errno == ERANGE || errno == EINVAL) { - return TSDB_CODE_FAILED; - } - while (n > 1 && z[n-1] == ' ') { - n--; - } - if (endPtr - z != n) { - return TSDB_CODE_FAILED; - } + if (n == 0) { + *value = 0; return TSDB_CODE_SUCCESS; } From 4737b00879433b5bc7892077ea4a6bf87643c658 Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Thu, 7 Dec 2023 14:00:50 +0800 Subject: [PATCH 09/10] cancel rm tail space --- source/common/src/tvariant.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/source/common/src/tvariant.c b/source/common/src/tvariant.c index 40de0428ff..dd5e8240b9 100644 --- a/source/common/src/tvariant.c +++ b/source/common/src/tvariant.c @@ -109,10 +109,7 @@ int32_t toDoubleEx(const char *z, int32_t n, uint32_t type, double* value) { if (errno == ERANGE || errno == EINVAL) { return TSDB_CODE_FAILED; } - // rm tail space - while (n > 1 && z[n-1] == ' ') { - n--; - } + if (endPtr - z != n) { return TSDB_CODE_FAILED; } @@ -151,11 +148,6 @@ int32_t toIntegerEx(const char *z, int32_t n, uint32_t type, int64_t *value) { return TSDB_CODE_SUCCESS; } - // rm tail space - while (n > 1 && z[n-1] == ' ') { - n--; - } - // 1. try to parse as integer *value = taosStr2Int64(z, &endPtr, 10); if (endPtr - z == n) { @@ -258,11 +250,6 @@ int32_t toUIntegerEx(const char *z, int32_t n, uint32_t type, uint64_t *value) { return TSDB_CODE_SUCCESS; } - // rm tail space - while (n > 1 && z[n-1] == ' ') { - n--; - } - // 1. parse as integer *value = taosStr2UInt64(p, &endPtr, 10); if (*p == '-' && *value) { From f23171d980e798468d97e4bf8152ccccc0f33341 Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Thu, 7 Dec 2023 16:06:09 +0800 Subject: [PATCH 10/10] adjust case --- tests/system-test/1-insert/insert_double.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/system-test/1-insert/insert_double.py b/tests/system-test/1-insert/insert_double.py index 56861f3cd4..b47b22ab44 100644 --- a/tests/system-test/1-insert/insert_double.py +++ b/tests/system-test/1-insert/insert_double.py @@ -40,8 +40,8 @@ class TDTestCase: # str support tdSql.execute(f"insert into {table_name} values(now, '-16', '+6')") - tdSql.execute(f"insert into {table_name} values(now, ' -80.99 ', ' +0042 ')") - tdSql.execute(f"insert into {table_name} values(now, ' -0042 ', ' +80.99 ')") + tdSql.execute(f"insert into {table_name} values(now, ' -80.99', ' +0042')") + tdSql.execute(f"insert into {table_name} values(now, ' -0042', ' +80.99')") tdSql.execute(f"insert into {table_name} values(now, '52.34354', '18.6')") tdSql.execute(f"insert into {table_name} values(now, '-12.', '+5.')") tdSql.execute(f"insert into {table_name} values(now, '-.12', '+.5')")