diff --git a/include/common/tdataformat.h b/include/common/tdataformat.h index 8f7f22a6a03ed91b8a0099b2022f395827f2e935..e70008e4ef1b0b4dbc8f76ce170842abdbfa4882 100644 --- a/include/common/tdataformat.h +++ b/include/common/tdataformat.h @@ -78,6 +78,7 @@ int32_t tEncodeTag(SEncoder *pEncoder, const STag *pTag); 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); // STRUCT ================= struct STColumn { diff --git a/source/common/src/tvariant.c b/source/common/src/tvariant.c index 7b0bef4918394b777ff8836245e1d22c14d156a8..0810be149716e58fdac74b67db6946fde7db62e9 100644 --- a/source/common/src/tvariant.c +++ b/source/common/src/tvariant.c @@ -155,7 +155,8 @@ void taosVariantCreateFromBinary(SVariant *pVar, const char *pz, size_t len, uin void taosVariantDestroy(SVariant *pVar) { if (pVar == NULL) return; - if (pVar->nType == TSDB_DATA_TYPE_BINARY || pVar->nType == TSDB_DATA_TYPE_NCHAR) { + if (pVar->nType == TSDB_DATA_TYPE_BINARY || pVar->nType == TSDB_DATA_TYPE_NCHAR + || pVar->nType == TSDB_DATA_TYPE_JSON) { taosMemoryFreeClear(pVar->pz); pVar->nLen = 0; } @@ -184,7 +185,8 @@ 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_NCHAR) { + if (pSrc->nType == TSDB_DATA_TYPE_BINARY || pSrc->nType == TSDB_DATA_TYPE_NCHAR + || pSrc->nType == TSDB_DATA_TYPE_JSON) { int32_t len = pSrc->nLen + TSDB_NCHAR_SIZE; char *p = taosMemoryRealloc(pDst->pz, len); assert(p); @@ -976,6 +978,7 @@ char *taosVariantGet(SVariant *pVar, int32_t type) { case TSDB_DATA_TYPE_FLOAT: return (char *)&pVar->d; case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_JSON: return (char *)pVar->pz; case TSDB_DATA_TYPE_NCHAR: return (char *)pVar->ucs4; diff --git a/source/libs/command/src/command.c b/source/libs/command/src/command.c index 377df8f8ace3674e10f2f4619c1ee62f3c3c2435..cd454c075b9430d72753d9e7bba2f355cbbebd1e 100644 --- a/source/libs/command/src/command.c +++ b/source/libs/command/src/command.c @@ -297,7 +297,7 @@ int32_t appendTagValues(char* buf, int32_t* len, STableCfg* pCfg) { SArray* pTagVals = NULL; STag* pTag = (STag*)pCfg->pTags; - if (pCfg->pTags && pTag->flags & TD_TAG_JSON) { + if (pCfg->pTags && tTagIsJson(pTag)) { char* pJson = parseTagDatatoJson(pTag); if (pJson) { *len += sprintf(buf + VARSTR_HEADER_SIZE + *len, "%s", pJson); diff --git a/source/libs/function/src/builtins.c b/source/libs/function/src/builtins.c index a0619bf9aa6acd420124e8c589e0e79a53ca3328..f1fbe89ab52df42f65de067a0a3221c8a617f00e 100644 --- a/source/libs/function/src/builtins.c +++ b/source/libs/function/src/builtins.c @@ -1543,7 +1543,7 @@ static int32_t translateToJson(SFunctionNode* pFunc, char* pErrBuf, int32_t len) return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName); } - pFunc->node.resType = (SDataType){.bytes = tDataTypes[TSDB_DATA_TYPE_BINARY].bytes, .type = TSDB_DATA_TYPE_BINARY}; + pFunc->node.resType = (SDataType){.bytes = tDataTypes[TSDB_DATA_TYPE_JSON].bytes, .type = TSDB_DATA_TYPE_JSON}; return TSDB_CODE_SUCCESS; } diff --git a/source/libs/nodes/src/nodesCloneFuncs.c b/source/libs/nodes/src/nodesCloneFuncs.c index b372bf75fc88c537b98fd1aa3a57eda079ccfd3d..543102e8fae107fdae711f23b5329d1ada61d1ff 100644 --- a/source/libs/nodes/src/nodesCloneFuncs.c +++ b/source/libs/nodes/src/nodesCloneFuncs.c @@ -18,6 +18,7 @@ #include "querynodes.h" #include "taos.h" #include "taoserror.h" +#include "tdatablock.h" #define COPY_SCALAR_FIELD(fldname) \ do { \ @@ -164,7 +165,15 @@ static int32_t valueNodeCopy(const SValueNode* pSrc, SValueNode* pDst) { memcpy(pDst->datum.p, pSrc->datum.p, len); break; } - case TSDB_DATA_TYPE_JSON: + case TSDB_DATA_TYPE_JSON:{ + int32_t len = getJsonValueLen(pSrc->datum.p); + pDst->datum.p = taosMemoryCalloc(1, len); + if (NULL == pDst->datum.p) { + return TSDB_CODE_OUT_OF_MEMORY; + } + memcpy(pDst->datum.p, pSrc->datum.p, len); + break; + } case TSDB_DATA_TYPE_DECIMAL: case TSDB_DATA_TYPE_BLOB: case TSDB_DATA_TYPE_MEDIUMBLOB: diff --git a/source/libs/nodes/src/nodesCodeFuncs.c b/source/libs/nodes/src/nodesCodeFuncs.c index df7429bd88cdb6b8029c1008d19258c05d7b192f..351cdd7b8fc03a6efac81cacd55a00e0283296a4 100644 --- a/source/libs/nodes/src/nodesCodeFuncs.c +++ b/source/libs/nodes/src/nodesCodeFuncs.c @@ -20,6 +20,7 @@ #include "querynodes.h" #include "taoserror.h" #include "tjson.h" +#include "tdatablock.h" static int32_t nodeToJson(const void* pObj, SJson* pJson); static int32_t jsonToNode(const SJson* pJson, void* pObj); @@ -2638,7 +2639,18 @@ static int32_t datumToJson(const void* pObj, SJson* pJson) { case TSDB_DATA_TYPE_VARBINARY: code = tjsonAddStringToObject(pJson, jkValueDatum, varDataVal(pNode->datum.p)); break; - case TSDB_DATA_TYPE_JSON: + case TSDB_DATA_TYPE_JSON:{ + int32_t len = getJsonValueLen(pNode->datum.p); + char* buf = taosMemoryCalloc( len * 2 + 1, sizeof(char)); + code = taosHexEncode(pNode->datum.p, buf, len); + if (code != TSDB_CODE_SUCCESS) { + taosMemoryFree(buf); + return TSDB_CODE_TSC_INVALID_VALUE; + } + code = tjsonAddStringToObject(pJson, jkValueDatum, buf); + taosMemoryFree(buf); + break; + } case TSDB_DATA_TYPE_DECIMAL: case TSDB_DATA_TYPE_BLOB: // todo @@ -2761,7 +2773,30 @@ static int32_t jsonToDatum(const SJson* pJson, void* pObj) { } break; } - case TSDB_DATA_TYPE_JSON: + case TSDB_DATA_TYPE_JSON:{ + pNode->datum.p = taosMemoryCalloc(1, pNode->node.resType.bytes); + if (NULL == pNode->datum.p) { + code = TSDB_CODE_OUT_OF_MEMORY; + break; + } + char* buf = taosMemoryCalloc(1, pNode->node.resType.bytes * 2 + 1); + if (NULL == buf) { + code = TSDB_CODE_OUT_OF_MEMORY; + break; + } + code = tjsonGetStringValue(pJson, jkValueDatum, buf); + if (code != TSDB_CODE_SUCCESS) { + taosMemoryFree(buf); + break; + } + code = taosHexDecode(buf, pNode->datum.p, pNode->node.resType.bytes); + if (code != TSDB_CODE_SUCCESS) { + taosMemoryFree(buf); + break; + } + taosMemoryFree(buf); + break; + } case TSDB_DATA_TYPE_DECIMAL: case TSDB_DATA_TYPE_BLOB: // todo diff --git a/source/libs/nodes/src/nodesUtilFuncs.c b/source/libs/nodes/src/nodesUtilFuncs.c index dc9d9b92ee8bb6414db9ce9b201cd9371d1acf77..d325179e4f533bc44745f0b8bfe4f243657d3f25 100644 --- a/source/libs/nodes/src/nodesUtilFuncs.c +++ b/source/libs/nodes/src/nodesUtilFuncs.c @@ -20,6 +20,7 @@ #include "taos.h" #include "taoserror.h" #include "thash.h" +#include "tdatablock.h" static SNode* makeNode(ENodeType type, size_t size) { SNode* p = taosMemoryCalloc(1, size); @@ -1675,6 +1676,10 @@ void nodesValueNodeToVariant(const SValueNode* pNode, SVariant* pVal) { pVal->pz[pVal->nLen + VARSTR_HEADER_SIZE] = 0; break; case TSDB_DATA_TYPE_JSON: + pVal->nLen = getJsonValueLen(pNode->datum.p); + pVal->pz = taosMemoryMalloc(pVal->nLen); + memcpy(pVal->pz, pNode->datum.p, pVal->nLen); + break; case TSDB_DATA_TYPE_DECIMAL: case TSDB_DATA_TYPE_BLOB: // todo diff --git a/source/libs/parser/inc/parUtil.h b/source/libs/parser/inc/parUtil.h index d06d6ec52724a397f3c279f1685f3b96c96dfdfa..e829c9266f5f82d131f50c83099b596a98a32125 100644 --- a/source/libs/parser/inc/parUtil.h +++ b/source/libs/parser/inc/parUtil.h @@ -62,7 +62,6 @@ int32_t getNumOfColumns(const STableMeta* pTableMeta); int32_t getNumOfTags(const STableMeta* pTableMeta); STableComInfo getTableInfo(const STableMeta* pTableMeta); STableMeta* tableMetaDup(const STableMeta* pTableMeta); -int32_t parseJsontoTagData(const char* json, SArray* pTagVals, STag** ppTag, SMsgBuf* pMsgBuf); int32_t trimString(const char* src, int32_t len, char* dst, int32_t dlen); diff --git a/source/libs/parser/src/parUtil.c b/source/libs/parser/src/parUtil.c index 202dd96581881273fcad1c3453e21feb6798db03..7a38ba84355b26a49ac395fe9fae07a34e68486e 100644 --- a/source/libs/parser/src/parUtil.c +++ b/source/libs/parser/src/parUtil.c @@ -219,6 +219,7 @@ int32_t buildInvalidOperationMsg(SMsgBuf* pBuf, const char* msg) { } int32_t buildSyntaxErrMsg(SMsgBuf* pBuf, const char* additionalInfo, const char* sourceStr) { + if(pBuf == NULL) return TSDB_CODE_TSC_SQL_SYNTAX_ERROR; const char* msgFormat1 = "syntax error near \'%s\'"; const char* msgFormat2 = "syntax error near \'%s\' (%s)"; const char* msgFormat3 = "%s"; @@ -346,7 +347,7 @@ static bool isValidateTag(char* input) { return true; } -int32_t parseJsontoTagData(const char* json, SArray* pTagVals, STag** ppTag, SMsgBuf* pMsgBuf) { +int32_t parseJsontoTagData(const char* json, SArray* pTagVals, STag** ppTag, void* pMsgBuf) { int32_t retCode = TSDB_CODE_SUCCESS; cJSON* root = NULL; SHashObj* keyHash = NULL; diff --git a/source/libs/qcom/src/queryUtil.c b/source/libs/qcom/src/queryUtil.c index 9ba149a33bbe516de0b39c58918e90fb046636d5..01e91e3046f55585b553c03b33db4a90fd05fa92 100644 --- a/source/libs/qcom/src/queryUtil.c +++ b/source/libs/qcom/src/queryUtil.c @@ -304,18 +304,21 @@ int32_t dataConverToStr(char* str, int type, void* buf, int32_t bufSize, int32_t char* parseTagDatatoJson(void* p) { char* string = NULL; - cJSON* json = cJSON_CreateObject(); - if (json == NULL) { - goto end; - } - SArray* pTagVals = NULL; + cJSON* json = NULL; if (tTagToValArray((const STag*)p, &pTagVals) != 0) { goto end; } int16_t nCols = taosArrayGetSize(pTagVals); + if (nCols == 0) { + goto end; + } char tagJsonKey[256] = {0}; + json = cJSON_CreateObject(); + if (json == NULL) { + goto end; + } for (int j = 0; j < nCols; ++j) { STagVal* pTagVal = (STagVal*)taosArrayGet(pTagVals, j); // json key encode by binary @@ -373,6 +376,10 @@ char* parseTagDatatoJson(void* p) { string = cJSON_PrintUnformatted(json); end: cJSON_Delete(json); + taosArrayDestroy(pTagVals); + if(string == NULL){ + string = strdup(TSDB_DATA_NULL_STR_L); + } return string; } diff --git a/source/libs/scalar/src/scalar.c b/source/libs/scalar/src/scalar.c index a0084553b963a62d904ebac466608fff2a9a7438..3d079d7bf1c49ec2a1de612143298fb26c880e7b 100644 --- a/source/libs/scalar/src/scalar.c +++ b/source/libs/scalar/src/scalar.c @@ -695,7 +695,11 @@ EDealRes sclRewriteFunction(SNode** pNode, SScalarCtx *ctx) { res->node.resType.scale = output.columnData->info.scale; res->node.resType.precision = output.columnData->info.precision; int32_t type = output.columnData->info.type; - if (IS_VAR_DATA_TYPE(type)) { + if (type == TSDB_DATA_TYPE_JSON){ + int32_t len = getJsonValueLen(output.columnData->pData); + res->datum.p = taosMemoryCalloc(len, 1); + memcpy(res->datum.p, output.columnData->pData, len); + } else if (IS_VAR_DATA_TYPE(type)) { res->datum.p = taosMemoryCalloc(res->node.resType.bytes + VARSTR_HEADER_SIZE + 1, 1); memcpy(res->datum.p, output.columnData->pData, varDataTLen(output.columnData->pData)); } else { diff --git a/source/libs/scalar/src/sclfunc.c b/source/libs/scalar/src/sclfunc.c index 76c0e4874045d503db45ee498c743149e9e2b4ee..3a219b78b5d87c288ec36280007a8c5595f53f1e 100644 --- a/source/libs/scalar/src/sclfunc.c +++ b/source/libs/scalar/src/sclfunc.c @@ -1152,42 +1152,30 @@ int32_t toJsonFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOu char tmp[TSDB_MAX_JSON_TAG_LEN] = {0}; for (int32_t i = 0; i < pInput[0].numOfRows; ++i) { - if (colDataIsNull_s(pInput[0].columnData, i)) { - colDataAppendNULL(pOutput->columnData, i); - continue; - } - char *input = pInput[0].columnData->pData + pInput[0].columnData->varmeta.offset[i]; + SArray* pTagVals = taosArrayInit(8, sizeof(STagVal)); + STag* pTag = NULL; - if(type == TSDB_DATA_TYPE_NCHAR){ - if (varDataTLen(input) > TSDB_MAX_JSON_TAG_LEN){ - colDataAppendNULL(pOutput->columnData, i); - continue; - } - int32_t len = taosUcs4ToMbs((TdUcs4 *)varDataVal(input), varDataLen(input), tmp); - if (len < 0) { - colDataAppendNULL(pOutput->columnData, i); - continue; - } - tmp[len] = 0; + if (colDataIsNull_s(pInput[0].columnData, i)) { + tTagNew(pTagVals, 1, true, &pTag); }else{ + char *input = pInput[0].columnData->pData + pInput[0].columnData->varmeta.offset[i]; if (varDataLen(input) > (TSDB_MAX_JSON_TAG_LEN - VARSTR_HEADER_SIZE) / TSDB_NCHAR_SIZE){ - colDataAppendNULL(pOutput->columnData, i); - continue; + taosArrayDestroy(pTagVals); + return TSDB_CODE_FAILED; } memcpy(tmp, varDataVal(input), varDataLen(input)); tmp[varDataLen(input)] = 0; + if(parseJsontoTagData(tmp, pTagVals, &pTag, NULL)){ + tTagNew(pTagVals, 1, true, &pTag); + } } - if(!tjsonValidateJson(tmp)){ - colDataAppendNULL(pOutput->columnData, i); - continue; - } - - colDataAppend(pOutput->columnData, i, input, false); + colDataAppend(pOutput->columnData, i, (const char*)pTag, false); + tTagFree(pTag); + taosArrayDestroy(pTagVals); } pOutput->numOfRows = pInput->numOfRows; - return TSDB_CODE_SUCCESS; } diff --git a/source/os/src/osString.c b/source/os/src/osString.c index da1fbd364fe4085404bf653921b85ef7002837ea..e6904f19634018180da9ea25a226a0545db67cf1 100644 --- a/source/os/src/osString.c +++ b/source/os/src/osString.c @@ -202,7 +202,7 @@ int32_t taosHexEncode(const char *src, char *dst, int32_t len) { } for (int32_t i = 0; i < len; ++i) { - sprintf(dst + i * 2, "%02x", src[i] & 0xff); + sprintf(dst + i * 2, "%02x", src[i]); } return 0; @@ -213,10 +213,10 @@ int32_t taosHexDecode(const char *src, char *dst, int32_t len) { return -1; } - uint16_t hn, ln, out; + uint8_t hn, ln, out; for (int i = 0, j = 0; i < len * 2; i += 2, ++j ) { - hn = src[i] > '9' ? src[i] - 'A' + 10 : src[i] - '0'; - ln = src[i + 1] > '9' ? src[i + 1] - 'A' + 10 : src[i + 1] - '0'; + hn = src[i] > '9' ? src[i] - 'a' + 10 : src[i] - '0'; + ln = src[i + 1] > '9' ? src[i + 1] - 'a' + 10 : src[i + 1] - '0'; out = (hn << 4) | ln; memcpy(dst + j, &out, 1); diff --git a/tests/system-test/2-query/json_tag.py b/tests/system-test/2-query/json_tag.py index 8a36ff0af4fda8b24178e500d6c0849b9dbd6772..0c48bd074f83a3506ab41729fa8e3bff45ea84db 100644 --- a/tests/system-test/2-query/json_tag.py +++ b/tests/system-test/2-query/json_tag.py @@ -684,6 +684,19 @@ class TDTestCase: tdSql.query("select ELAPSED(ts,1h) from jsons1 where jtag->'tag1'>1;") tdSql.checkRows(1) + # to_json() + tdSql.query("select to_json('{\"abc\":123}') from jsons1_1") + tdSql.checkRows(2) + tdSql.checkData(0, 0, '{"abc":123}') + tdSql.checkData(1, 0, '{"abc":123}') + tdSql.query("select to_json('null') from jsons1_1") + tdSql.checkRows(2) + tdSql.checkData(0, 0, 'null') + tdSql.checkData(1, 0, 'null') + tdSql.query("select to_json('{\"key\"}') from jsons1_1") + tdSql.checkRows(2) + tdSql.checkData(0, 0, 'null') + tdSql.checkData(1, 0, 'null') #test TD-12077 tdSql.execute("insert into jsons1_16 using jsons1 tags('{\"tag1\":\"收到货\",\"tag2\":\"\",\"tag3\":-2.111}') values(1591062628000, 2, NULL, '你就会', 'dws')")