Merge pull request #23912 from taosdata/enh/3.0/TD-27276

enh : accept float data as int field input
This commit is contained in:
dapan1121 2023-12-07 19:14:59 +08:00 committed by GitHub
commit 4d993e8f9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 785 additions and 59 deletions

View File

@ -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)

View File

@ -37,6 +37,10 @@ typedef struct SVariant {
};
} SVariant;
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);

View File

@ -19,6 +19,278 @@
#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 parseSignAndUInteger(const char *z, int32_t n, bool *is_neg, uint64_t *value) {
// 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] != '.' && (z[0] < '0' || z[0] > '9')) {
return TSDB_CODE_FAILED;
}
if (has_sign) {
if (n < 2) {
return TSDB_CODE_FAILED;
}
z++;
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
return parseBinaryUInteger(z, n, value);
}
if (z[1] == 'x' || z[1] == 'X') {
// parsing as hex
*value = taosStr2UInt64(z, &endPtr, 16);
parsed = true;
}
}
if (!parsed) {
// 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;
}
return TSDB_CODE_SUCCESS;
}
int32_t toDoubleEx(const char *z, int32_t n, uint32_t type, double* value) {
if (n == 0) {
*value = 0;
return TSDB_CODE_SUCCESS;
}
errno = 0;
char* endPtr = NULL;
*value = taosStr2Double(z, &endPtr);
if (errno == ERANGE || errno == EINVAL) {
return TSDB_CODE_FAILED;
}
if (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;
} break;
case TK_NK_FLOAT: {
double val = round(taosStr2Double(z, &endPtr));
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 (n == 0) {
*value = 0;
return TSDB_CODE_SUCCESS;
}
// 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;
} 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
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 (is_neg) {
if (uv > 1ull + INT64_MAX) {
*value = INT64_MIN;
return TSDB_CODE_FAILED;
} else {
*value = -uv;
}
} else {
if (uv > INT64_MAX) {
*value = INT64_MAX;
return TSDB_CODE_FAILED;
}
*value = uv;
}
}
return code;
}
int32_t toUIntegerEx(const char *z, int32_t n, uint32_t type, uint64_t *value) {
errno = 0;
char *endPtr = NULL;
const char *p = z;
while (*p == ' ') {
p++;
}
switch (type) {
case TK_NK_INTEGER: {
*value = taosStr2UInt64(p, &endPtr, 10);
if (*p == '-' && *value) {
return TSDB_CODE_FAILED;
}
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;
return TSDB_CODE_SUCCESS;
} break;
default:
break;
}
if (n == 0) {
*value = 0;
return TSDB_CODE_SUCCESS;
}
// 1. parse as integer
*value = taosStr2UInt64(p, &endPtr, 10);
if (*p == '-' && *value) {
return TSDB_CODE_FAILED;
}
if (endPtr - z == n) {
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
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;
}
int32_t toInteger(const char *z, int32_t n, int32_t base, int64_t *value) {
errno = 0;
char *endPtr = NULL;
@ -26,10 +298,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 +311,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 +418,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 +442,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) {

View File

@ -14,6 +14,7 @@
#include "tdef.h"
#include "tvariant.h"
#include "ttime.h"
#include "ttokendef.h"
namespace {
//
@ -24,40 +25,177 @@ int main(int argc, char** argv) {
return RUN_ALL_TESTS();
}
TEST(testCase, toInteger_test) {
TEST(testCase, toUIntegerEx_test) {
uint64_t val = 0;
char* s = "123";
uint32_t type = 0;
int64_t val = 0;
int32_t ret = toInteger(s, strlen(s), 10, &val);
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 = toInteger(s, strlen(s), 10, &val);
ret = toUIntegerEx(s, strlen(s), TK_NK_INTEGER, &val);
ASSERT_EQ(ret, 0);
ASSERT_EQ(val, 9223372036854775807);
s = "9323372036854775807";
ret = toInteger(s, strlen(s), 10, &val);
// UINT64_MAX
s = "18446744073709551615";
ret = toUIntegerEx(s, strlen(s), TK_NK_INTEGER, &val);
ASSERT_EQ(ret, 0);
ASSERT_EQ(val, 9323372036854775807u);
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), TK_NK_INTEGER, &val);
ASSERT_EQ(ret, 0);
ASSERT_EQ(val, 123);
s = "-1";
ret = toIntegerEx(s, strlen(s), TK_NK_INTEGER, &val);
ASSERT_EQ(ret, 0);
ASSERT_EQ(val, -1);
s = "1000u";
ret = toIntegerEx(s, strlen(s), TK_NK_INTEGER, &val);
ASSERT_EQ(ret, -1);
s = "0x1f";
ret = toIntegerEx(s, strlen(s), TK_NK_HEX, &val);
ASSERT_EQ(ret, 0);
ASSERT_EQ(val, 31);
s = "-0x40";
ret = toIntegerEx(s, strlen(s), TK_NK_HEX, &val);
ASSERT_EQ(ret, 0);
ASSERT_EQ(val, -64);
s = "0b110";
ret = toIntegerEx(s, strlen(s), TK_NK_BIN, &val);
ASSERT_EQ(ret, 0);
ASSERT_EQ(val, 6);
s = "-0b10010";
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), TK_NK_FLOAT, &val);
ASSERT_EQ(ret, 0);
ASSERT_EQ(val, -52343544534);
s = "1.869895343e4";
ret = toIntegerEx(s, strlen(s), TK_NK_FLOAT, &val);
ASSERT_EQ(ret, 0);
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 = toInteger(s, strlen(s), 10, &val);
ret = toIntegerEx(s, strlen(s), TK_NK_INTEGER, &val);
ASSERT_EQ(ret, -1);
// UINT64_MAX
s = "18446744073709551615";
ret = toIntegerEx(s, strlen(s), TK_NK_INTEGER, &val);
ASSERT_EQ(ret, -1);
}
TEST(testCase, toInteger_test) {
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 = "-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);
@ -77,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);
}

View File

@ -507,7 +507,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, pToken->type, &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);
@ -518,7 +519,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, pToken->type, &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);
@ -528,7 +530,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, pToken->type, &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);
@ -538,7 +541,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, pToken->type, &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);
@ -548,7 +552,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, pToken->type, &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);
@ -558,7 +563,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, pToken->type, &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);
@ -568,7 +574,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, pToken->type, &iv);
if (TSDB_CODE_SUCCESS != code) {
return buildSyntaxErrMsg(pMsgBuf, "invalid bigint data", pToken->z);
}
val->i64 = iv;
@ -576,7 +583,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, pToken->type, &uv);
if (TSDB_CODE_SUCCESS != code) {
return buildSyntaxErrMsg(pMsgBuf, "invalid unsigned bigint data", pToken->z);
}
*(uint64_t*)(&val->i64) = uv;
@ -585,11 +593,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;
@ -598,8 +606,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);
@ -1431,7 +1440,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, 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)) {
return buildSyntaxErrMsg(&pCxt->msg, "tinyint data overflow", pToken->z);
@ -1439,7 +1449,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, 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) {
return buildSyntaxErrMsg(&pCxt->msg, "unsigned tinyint data overflow", pToken->z);
@ -1447,7 +1458,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, 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)) {
return buildSyntaxErrMsg(&pCxt->msg, "smallint data overflow", pToken->z);
@ -1455,7 +1467,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, 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) {
return buildSyntaxErrMsg(&pCxt->msg, "unsigned smallint data overflow", pToken->z);
@ -1463,7 +1476,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, 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)) {
return buildSyntaxErrMsg(&pCxt->msg, "int data overflow", pToken->z);
@ -1471,7 +1485,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, 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) {
return buildSyntaxErrMsg(&pCxt->msg, "unsigned int data overflow", pToken->z);
@ -1479,25 +1494,26 @@ 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, 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: {
if (TSDB_CODE_SUCCESS != toUInteger(pToken->z, pToken->n, 10, &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 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;
@ -1505,12 +1521,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;

View File

@ -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;
}

View File

@ -250,6 +250,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

View File

@ -0,0 +1,136 @@
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, -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)")
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)
# 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})")
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())

View File

@ -0,0 +1,146 @@
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.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.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 {self.database}")
tdSql.execute(f"use {self.database}")
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 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(0, 4294967295))
rows.append(r)
writer.writerows(rows)
f.close()
print(f"{filepath} ready!")
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:
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
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.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, 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()
tdLog.success(f"{__file__} successfully executed")
tdCases.addLinux(__file__, TDTestCase())
tdCases.addWindows(__file__, TDTestCase())