From 6f3c6548cbe5b1635b8525db0fbf5b7b008dfa7e Mon Sep 17 00:00:00 2001 From: Pengrongkun Date: Wed, 13 Nov 2024 16:53:15 +0800 Subject: [PATCH] add geometry check during stmt_bind_param --- include/common/tdataformat.h | 1 + source/common/CMakeLists.txt | 1 + source/common/src/tdataformat.c | 65 +++++++++++++++-- source/libs/parser/src/parInsertStmt.c | 41 +++++++++-- source/libs/parser/src/parUtil.c | 30 ++++++++ tests/script/api/stmt2-geometry-test.c | 99 ++++++++++++++++++++++++++ 6 files changed, 226 insertions(+), 11 deletions(-) create mode 100644 tests/script/api/stmt2-geometry-test.c diff --git a/include/common/tdataformat.h b/include/common/tdataformat.h index 19f3e222d1..cc578fbee9 100644 --- a/include/common/tdataformat.h +++ b/include/common/tdataformat.h @@ -155,6 +155,7 @@ int32_t tDecodeTag(SDecoder *pDecoder, STag **ppTag); int32_t tTagToValArray(const STag *pTag, SArray **ppArray); void debugPrintSTag(STag *pTag, const char *tag, int32_t ln); // TODO: remove int32_t parseJsontoTagData(const char *json, SArray *pTagVals, STag **ppTag, void *pMsgBuf); +int32_t parseGeotoTagData(const char *g, SArray *pTagVals, SSchema *pTagSchema, unsigned char *output); // SColData ================================ typedef struct { diff --git a/source/common/CMakeLists.txt b/source/common/CMakeLists.txt index f10eb6a611..e9b8727ce8 100644 --- a/source/common/CMakeLists.txt +++ b/source/common/CMakeLists.txt @@ -49,6 +49,7 @@ target_link_libraries( PUBLIC os PUBLIC util INTERFACE api + PRIVATE geometry ) if(${BUILD_S3}) diff --git a/source/common/src/tdataformat.c b/source/common/src/tdataformat.c index e580ad33bd..7a6557fd28 100644 --- a/source/common/src/tdataformat.c +++ b/source/common/src/tdataformat.c @@ -15,6 +15,7 @@ #define _DEFAULT_SOURCE #include "tdataformat.h" +#include "geosWrapper.h" #include "tRealloc.h" #include "tdatablock.h" #include "tlog.h" @@ -3036,6 +3037,35 @@ _exit: return code; } +int32_t formatGeometry(SColData *pColData, int8_t *buf, int32_t lenght, int32_t buffMaxLen) { + int32_t code = 0; + unsigned char *output = NULL; + size_t size = 0; + uint8_t *g = NULL; + code = tRealloc(&g, lenght); + if (code) { + return code; + } + + (void)memcpy(g, buf, lenght); + code = doGeomFromText(g, &output, &size); + tFree(g); + + if (code != TSDB_CODE_SUCCESS) { + goto _exit; + } + if (size > buffMaxLen) { + code = TSDB_CODE_PAR_VALUE_TOO_LONG; + goto _exit; + } + + code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_VALUE](pColData, output, size); + +_exit: + geosFreeBuffer(output); + return code; +} + int32_t tColDataAddValueByBind(SColData *pColData, TAOS_MULTI_BIND *pBind, int32_t buffMaxLen) { int32_t code = 0; @@ -3046,6 +3076,13 @@ int32_t tColDataAddValueByBind(SColData *pColData, TAOS_MULTI_BIND *pBind, int32 } if (IS_VAR_DATA_TYPE(pColData->type)) { // var-length data type + if (pColData->type == TSDB_DATA_TYPE_GEOMETRY) { + code = initCtxGeomFromText(); + if (code != TSDB_CODE_SUCCESS) { + uError("init geom from text failed, code:%d", code); + return code; + } + } for (int32_t i = 0; i < pBind->num; ++i) { if (pBind->is_null && pBind->is_null[i]) { if (pColData->cflag & COL_IS_KEY) { @@ -3055,11 +3092,15 @@ int32_t tColDataAddValueByBind(SColData *pColData, TAOS_MULTI_BIND *pBind, int32 code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NULL](pColData, NULL, 0); if (code) goto _exit; } else if (pBind->length[i] > buffMaxLen) { - uError("var data length too big, len:%d, max:%d", pBind->length[i], buffMaxLen); - return TSDB_CODE_INVALID_PARA; + return TSDB_CODE_PAR_VALUE_TOO_LONG; } else { - code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_VALUE]( - pColData, (uint8_t *)pBind->buffer + pBind->buffer_length * i, pBind->length[i]); + if (pColData->type == TSDB_DATA_TYPE_GEOMETRY) { + code = formatGeometry(pColData, (uint8_t *)pBind->buffer + pBind->buffer_length * i, pBind->length[i], + buffMaxLen); + } else { + code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_VALUE]( + pColData, (uint8_t *)pBind->buffer + pBind->buffer_length * i, pBind->length[i]); + } } } } else { // fixed-length data type @@ -3119,6 +3160,13 @@ int32_t tColDataAddValueByBind2(SColData *pColData, TAOS_STMT2_BIND *pBind, int3 if (IS_VAR_DATA_TYPE(pColData->type)) { // var-length data type uint8_t *buf = pBind->buffer; + if (pColData->type == TSDB_DATA_TYPE_GEOMETRY) { + code = initCtxGeomFromText(); + if (code != TSDB_CODE_SUCCESS) { + uError("init geom from text failed, code:%d", code); + return code; + } + } for (int32_t i = 0; i < pBind->num; ++i) { if (pBind->is_null && pBind->is_null[i]) { if (pColData->cflag & COL_IS_KEY) { @@ -3133,10 +3181,13 @@ int32_t tColDataAddValueByBind2(SColData *pColData, TAOS_STMT2_BIND *pBind, int3 if (code) goto _exit; } } else if (pBind->length[i] > buffMaxLen) { - uError("var data length too big, len:%d, max:%d", pBind->length[i], buffMaxLen); - return TSDB_CODE_INVALID_PARA; + return TSDB_CODE_PAR_VALUE_TOO_LONG; } else { - code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_VALUE](pColData, buf, pBind->length[i]); + if (pColData->type == TSDB_DATA_TYPE_GEOMETRY) { + code = formatGeometry(pColData, buf, pBind->length[i], buffMaxLen); + } else { + code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_VALUE](pColData, buf, pBind->length[i]); + } buf += pBind->length[i]; } } diff --git a/source/libs/parser/src/parInsertStmt.c b/source/libs/parser/src/parInsertStmt.c index 0979028e6d..9e78d53cc9 100644 --- a/source/libs/parser/src/parInsertStmt.c +++ b/source/libs/parser/src/parInsertStmt.c @@ -13,6 +13,7 @@ * along with this program. If not, see . */ +#include "geosWrapper.h" #include "os.h" #include "parInsertUtil.h" #include "parInt.h" @@ -187,11 +188,24 @@ int32_t qBindStmtTagsValue(void* pBlock, void* boundTags, int64_t suid, const ch if (code != TSDB_CODE_SUCCESS) { goto end; } + } else if (pTagSchema->type == TSDB_DATA_TYPE_GEOMETRY) { + char* tmp = taosMemoryCalloc(1, colLen); + if (!tmp) { + code = terrno; + goto end; + } + memcpy(tmp, bind[c].buffer, colLen); + unsigned char* out = NULL; + code = parseGeotoTagData(tmp, pTagArray, pTagSchema, out); + taosMemoryFree(tmp); + if (code != TSDB_CODE_SUCCESS) { + geosFreeBuffer(out); + goto end; + } } else { STagVal val = {.cid = pTagSchema->colId, .type = pTagSchema->type}; // strcpy(val.colName, pTagSchema->name); - if (pTagSchema->type == TSDB_DATA_TYPE_BINARY || pTagSchema->type == TSDB_DATA_TYPE_VARBINARY || - pTagSchema->type == TSDB_DATA_TYPE_GEOMETRY) { + if (pTagSchema->type == TSDB_DATA_TYPE_BINARY || pTagSchema->type == TSDB_DATA_TYPE_VARBINARY) { val.pData = (uint8_t*)bind[c].buffer; val.nData = colLen; } else if (pTagSchema->type == TSDB_DATA_TYPE_NCHAR) { @@ -251,6 +265,9 @@ end: if (p->type == TSDB_DATA_TYPE_NCHAR) { taosMemoryFreeClear(p->pData); } + if (p->type == TSDB_DATA_TYPE_GEOMETRY) { + geosFreeBuffer(p->pData); + } } taosArrayDestroy(pTagArray); taosArrayDestroy(tagName); @@ -539,11 +556,24 @@ int32_t qBindStmtTagsValue2(void* pBlock, void* boundTags, int64_t suid, const c if (code != TSDB_CODE_SUCCESS) { goto end; } + } else if (pTagSchema->type == TSDB_DATA_TYPE_GEOMETRY) { + char* tmp = taosMemoryCalloc(1, colLen); + if (!tmp) { + code = terrno; + goto end; + } + memcpy(tmp, bind[c].buffer, colLen); + unsigned char* out = NULL; + code = parseGeotoTagData(tmp, pTagArray, pTagSchema, out); + taosMemoryFree(tmp); + if (code != TSDB_CODE_SUCCESS) { + geosFreeBuffer(out); + goto end; + } } else { STagVal val = {.cid = pTagSchema->colId, .type = pTagSchema->type}; // strcpy(val.colName, pTagSchema->name); - if (pTagSchema->type == TSDB_DATA_TYPE_BINARY || pTagSchema->type == TSDB_DATA_TYPE_VARBINARY || - pTagSchema->type == TSDB_DATA_TYPE_GEOMETRY) { + if (pTagSchema->type == TSDB_DATA_TYPE_BINARY || pTagSchema->type == TSDB_DATA_TYPE_VARBINARY) { val.pData = (uint8_t*)bind[c].buffer; val.nData = colLen; } else if (pTagSchema->type == TSDB_DATA_TYPE_NCHAR) { @@ -603,6 +633,9 @@ end: if (p->type == TSDB_DATA_TYPE_NCHAR) { taosMemoryFreeClear(p->pData); } + if (p->type == TSDB_DATA_TYPE_GEOMETRY) { + geosFreeBuffer(p->pData); + } } taosArrayDestroy(pTagArray); taosArrayDestroy(tagName); diff --git a/source/libs/parser/src/parUtil.c b/source/libs/parser/src/parUtil.c index e35eea9e72..fc6ed0d7b0 100644 --- a/source/libs/parser/src/parUtil.c +++ b/source/libs/parser/src/parUtil.c @@ -15,6 +15,7 @@ #include "parUtil.h" #include "cJSON.h" +#include "geosWrapper.h" #include "querynodes.h" #include "tarray.h" #include "tlog.h" @@ -518,6 +519,35 @@ end: return retCode; } +int32_t parseGeotoTagData(const char* g, SArray* pTagVals, SSchema* pTagSchema, unsigned char* output) { + int32_t code = 0; + size_t size = 0; + STagVal val = {.cid = pTagSchema->colId, .type = pTagSchema->type}; + code = initCtxGeomFromText(); + if (code != TSDB_CODE_SUCCESS) { + goto end; + } + code = doGeomFromText(g, &output, &size); + if (code != TSDB_CODE_SUCCESS) { + goto end; + } + if (size > pTagSchema->bytes) { + code = TSDB_CODE_PAR_VALUE_TOO_LONG; + goto end; + } + + val.pData = (uint8_t*)output; + val.nData = (uint32_t)size; + + if (NULL == taosArrayPush(pTagVals, &val)) { + code = terrno; + goto end; + } + +end: + return code; +} + static int32_t getInsTagsTableTargetNameFromOp(int32_t acctId, SOperatorNode* pOper, SName* pName) { if (OP_TYPE_EQUAL != pOper->opType) { return TSDB_CODE_SUCCESS; diff --git a/tests/script/api/stmt2-geometry-test.c b/tests/script/api/stmt2-geometry-test.c new file mode 100644 index 0000000000..bb53e6e42b --- /dev/null +++ b/tests/script/api/stmt2-geometry-test.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include "taos.h" + +void do_query(TAOS* taos, const char* sql) { + printf("[sql]%s\n", sql); + TAOS_RES* result = taos_query(taos, sql); + int code = taos_errno(result); + if (code) { + printf(" failed to query: %s, reason:%s\n", sql, taos_errstr(result)); + taos_free_result(result); + return; + } + taos_free_result(result); +} + +void execute_test(TAOS* taos, const char* tbname, const char* tag2, const char* col2, const char* case_desc) { + // prepare stmt + TAOS_STMT2_OPTION option = {0, true, false, NULL, NULL}; + TAOS_STMT2* stmt = taos_stmt2_init(taos, &option); + const char* sql; + if (tbname == "tb4") { + sql = "insert into db.? using db.stb2 tags(?, ?) values(?,?)"; + } else { + sql = "insert into db.? using db.stb tags(?, ?) values(?,?)"; + } + int code = taos_stmt2_prepare(stmt, sql, 0); + printf("\n%s\n insert into db.? using db.stb tags(?, ?) values(?,?)\n bind_tag : %s, bind_col : %s\n", case_desc, + tag2, col2); + if (code != 0) { + printf(" failed to execute taos_stmt2_prepare. error:%s\n", taos_stmt2_error(stmt)); + taos_stmt2_close(stmt); + return; + } + + // prepare data + int t1_val = 0; + int64_t ts = 1591060628000; + int32_t length[5] = {sizeof(int), 2, sizeof(int64_t), (int32_t)strlen(tag2), (int32_t)strlen(col2)}; + TAOS_STMT2_BIND tags[2] = {{TSDB_DATA_TYPE_INT, &t1_val, &length[0], NULL, 1}, + {TSDB_DATA_TYPE_GEOMETRY, (void*)tag2, &length[3], NULL, 1}}; + TAOS_STMT2_BIND params[2] = {{TSDB_DATA_TYPE_TIMESTAMP, &ts, &length[2], NULL, 1}, + {TSDB_DATA_TYPE_GEOMETRY, (void*)col2, &length[4], NULL, 1}}; + TAOS_STMT2_BIND* tagv[1] = {&tags[0]}; + TAOS_STMT2_BIND* paramv[1] = {¶ms[0]}; + + TAOS_STMT2_BINDV bindv = {1, &tbname, &tagv[0], ¶mv[0]}; + code = taos_stmt2_bind_param(stmt, &bindv, -1); + if (code != 0) { + printf(" failed to bind param. error:%s\n", taos_stmt2_error(stmt)); + taos_stmt2_close(stmt); + return; + } + + if (taos_stmt2_exec(stmt, NULL)) { + printf(" failed to execute insert statement.error:%s\n", taos_stmt2_error(stmt)); + taos_stmt2_close(stmt); + return; + } + printf("[ok]\n"); + + taos_stmt2_close(stmt); +} + +void test1(TAOS* taos) { + execute_test(taos, "tb1", "POINT(1.0 1.0)", "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", "[normal]case 1"); +} + +void test2(TAOS* taos) { + execute_test(taos, "tb2", "hello", "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", "[wrong tag]case 2"); +} + +void test3(TAOS* taos) { execute_test(taos, "tb3", "POLYGON((0 0, 4 0, 4 4, 0 4, 0 0))", "1", "[wrong col]case 3"); } + +void test4(TAOS* taos) { + execute_test(taos, "tb4", "POLYGON((0 0, 4 0, 4 4, 0 4, 0 0))", "POINT(1.0 1.0)", "[wrong size]case 4"); +} + +int main() { + TAOS* taos = taos_connect("localhost", "root", "taosdata", "", 0); + if (!taos) { + printf("failed to connect to db, reason:%s\n", taos_errstr(taos)); + exit(1); + } + // init test db & stb table + do_query(taos, "drop database if exists db"); + do_query(taos, "create database db"); + do_query(taos, "create table db.stb (ts timestamp, b geometry(100)) tags(t1 int, t2 geometry(100))"); + do_query(taos, "create table db.stb2 (ts timestamp, b geometry(100)) tags(t1 int, t2 geometry(10))"); + + test1(taos); + test2(taos); + test3(taos); + test4(taos); + + taos_close(taos); + taos_cleanup(); +}