From d3e86d9854af18e17fa2ae6dfc85b4b5131d7f00 Mon Sep 17 00:00:00 2001 From: factosea <285808407@qq.com> Date: Mon, 30 Dec 2024 18:21:26 +0800 Subject: [PATCH] cols --- include/util/taoserror.h | 3 + source/libs/function/src/builtins.c | 5 + source/libs/parser/src/parTranslater.c | 352 +++++++++++++-------- source/util/src/terror.c | 4 + tests/system-test/2-query/cols_function.py | 1 + 5 files changed, 233 insertions(+), 132 deletions(-) diff --git a/include/util/taoserror.h b/include/util/taoserror.h index 366ffc6ecc..2410016485 100644 --- a/include/util/taoserror.h +++ b/include/util/taoserror.h @@ -907,6 +907,9 @@ int32_t taosGetErrSize(); #define TSDB_CODE_PAR_INVALID_FORECAST_CLAUSE TAOS_DEF_ERROR_CODE(0, 0x2685) #define TSDB_CODE_PAR_INVALID_VGID_LIST TAOS_DEF_ERROR_CODE(0, 0x2686) #define TSDB_CODE_PAR_INVALID_COLS_FUNCTION TAOS_DEF_ERROR_CODE(0, 0x2687) +#define TSDB_CODE_PAR_INVALID_COLS_SELECTFUNC TAOS_DEF_ERROR_CODE(0, 0x2688) +#define TSDB_CODE_INVALID_MULITI_COLS_FUNC TAOS_DEF_ERROR_CODE(0, 0x2688) +#define TSDB_CODE_INVALID_COLS_ALIAS TAOS_DEF_ERROR_CODE(0, 0x2689) #define TSDB_CODE_PAR_INTERNAL_ERROR TAOS_DEF_ERROR_CODE(0, 0x26FF) //planner diff --git a/source/libs/function/src/builtins.c b/source/libs/function/src/builtins.c index 6d4e2c5bdb..16eb418ce5 100644 --- a/source/libs/function/src/builtins.c +++ b/source/libs/function/src/builtins.c @@ -1696,6 +1696,10 @@ static int32_t translateOutVarchar(SFunctionNode* pFunc, char* pErrBuf, int32_t return TSDB_CODE_SUCCESS; } +static int32_t translateColsFunction(SFunctionNode* pFunc, char* pErrBuf, int32_t len) { + return TSDB_CODE_PAR_INVALID_COLS_FUNCTION; +} + static int32_t translateHistogramImpl(SFunctionNode* pFunc, char* pErrBuf, int32_t len) { FUNC_ERR_RET(validateParam(pFunc, pErrBuf, len)); int32_t numOfParams = LIST_LENGTH(pFunc->pParameterList); @@ -5644,6 +5648,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = { }, { .name = "cols", + .translateFunc = translateColsFunction, }, }; // clang-format on diff --git a/source/libs/parser/src/parTranslater.c b/source/libs/parser/src/parTranslater.c index 1680808c71..1ccfce3e0a 100755 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -1076,6 +1076,14 @@ static bool isForecastPseudoColumnFunc(const SNode* pNode) { return (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsForecastPseudoColumnFunc(((SFunctionNode*)pNode)->funcId)); } +static bool isColsFunctionResult(const SNode* pNode) { + return ((nodesIsExprNode(pNode)) && ((SExprNode*)pNode)->bindTupleFuncIdx > 0); +} + +static bool isInvalidColsBindFunction(const SFunctionNode* pFunc) { + return (!fmIsSelectFunc(pFunc->funcId) && pFunc->node.tupleFuncIdx != 0); +} + #ifdef BUILD_NO_CALL static bool isTimelineFunc(const SNode* pNode) { return (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsTimelineFunc(((SFunctionNode*)pNode)->funcId)); @@ -2311,7 +2319,7 @@ static EDealRes translateOperator(STranslateContext* pCxt, SOperatorNode* pOp) { static EDealRes haveVectorFunction(SNode* pNode, void* pContext) { if (isAggFunc(pNode) || isIndefiniteRowsFunc(pNode) || isWindowPseudoColumnFunc(pNode) || - isInterpPseudoColumnFunc(pNode) || isForecastPseudoColumnFunc(pNode)) { + isInterpPseudoColumnFunc(pNode) || isForecastPseudoColumnFunc(pNode) || isColsFunctionResult(pNode)) { *((bool*)pContext) = true; return DEAL_RES_END; } @@ -3298,6 +3306,10 @@ static EDealRes translateFunction(STranslateContext* pCxt, SFunctionNode** pFunc pCxt->errCode = TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION; } } + if (isInvalidColsBindFunction(*pFunc)) { + pCxt->errCode = TSDB_CODE_PAR_INVALID_COLS_SELECTFUNC; + return DEAL_RES_ERROR; + } if (TSDB_CODE_SUCCESS == pCxt->errCode) { pCxt->errCode = translateFunctionImpl(pCxt, pFunc); } @@ -3813,7 +3825,7 @@ static EDealRes doCheckExprForGroupBy(SNode** pNode, void* pContext) { if (isVectorFunc(*pNode) && !isDistinctOrderBy(pCxt)) { return DEAL_RES_IGNORE_CHILD; } - if (isColsFunc(*pNode)) { + if (isColsFunctionResult(*pNode)) { return DEAL_RES_IGNORE_CHILD; } SNode* pGroupNode = NULL; @@ -3918,10 +3930,10 @@ static EDealRes doCheckAggColCoexist(SNode** pNode, void* pContext) { if (isVectorFunc(*pNode)) { return DEAL_RES_IGNORE_CHILD; } - if(isColsFunc(*pNode)) { + if(isColsFunctionResult(*pNode)) { pCxt->hasColFunc = true; - return DEAL_RES_IGNORE_CHILD; } + SNode* pPartKey = NULL; bool partionByTbname = false; if (fromSingleTable(((SSelectStmt*)pCxt->pTranslateCxt->pCurrStmt)->pFromTable) || @@ -7299,8 +7311,9 @@ static int32_t translateSelectWithoutFrom(STranslateContext* pCxt, SSelectStmt* return translateExprList(pCxt, pSelect->pProjectionList); } typedef struct SCheckColsFuncCxt { - bool hasColsFunc; - bool hasMultiColsFunc; + bool hasColsFunc; + SNodeList** selectFuncList; + int32_t status; } SCheckColsFuncCxt; static bool isColsFuncByName(SFunctionNode* pFunc) { @@ -7310,26 +7323,16 @@ static bool isColsFuncByName(SFunctionNode* pFunc) { return true; } -static bool isMultiColsFunc(SFunctionNode* pFunc) { - if (strcasecmp(pFunc->functionName, "cols") != 0) { - return false; - } - return pFunc->pParameterList->length > 2; -} - -static EDealRes isMultiColsFuncNode(SNode** pNode, void* pContext) { - SCheckColsFuncCxt* pCxt = pContext; - if (QUERY_NODE_FUNCTION == nodeType(*pNode)) { - SFunctionNode* pFunc = (SFunctionNode*)*pNode; +static bool isMultiColsFuncNode(SNode* pNode) { + if (QUERY_NODE_FUNCTION == nodeType(pNode)) { + SFunctionNode* pFunc = (SFunctionNode*)pNode; if (isColsFuncByName(pFunc)) { - pCxt->hasColsFunc = true; if (pFunc->pParameterList->length > 2) { - pCxt->hasMultiColsFunc = true; - return DEAL_RES_END; + return true; } } } - return DEAL_RES_CONTINUE; + return false; } typedef struct SBindTupleFuncCxt { @@ -7347,16 +7350,6 @@ static EDealRes pushDownBindSelectFunc(SNode** pNode, void* pContext) { return DEAL_RES_CONTINUE; } -static void checkColsFuncInList(SNodeList* nodeList, SCheckColsFuncCxt* pCheckouColsFuncCxt) { - nodesRewriteExprs(nodeList, isMultiColsFuncNode, pCheckouColsFuncCxt); - return; -} - -static void checkColsFunc(SNode** pNode, SCheckColsFuncCxt* pCheckouColsFuncCxt) { - nodesRewriteExpr(pNode, isMultiColsFuncNode, pCheckouColsFuncCxt); - return; -} - static bool invalidColsAlias(SFunctionNode* pFunc) { if (pFunc->node.asAlias) { if (pFunc->pParameterList->length > 2) { @@ -7374,37 +7367,6 @@ static bool invalidColsAlias(SFunctionNode* pFunc) { return false; } -static int32_t hasInvalidColsFunction(STranslateContext* pCxt, SNodeList* nodeList, - SCheckColsFuncCxt* pCheckouColsFuncCxt) { - SNode* pTmpNode = NULL; - FOREACH(pTmpNode, nodeList) { - if (QUERY_NODE_FUNCTION == nodeType(pTmpNode)) { - SFunctionNode* pFunc = (SFunctionNode*)pTmpNode; - if (isColsFuncByName(pFunc)) { - pCheckouColsFuncCxt->hasColsFunc = true; - if (invalidColsAlias(pFunc)) { - return generateSyntaxErrMsgExt(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_COLS_FUNCTION, - "Invalid using alias for cols function"); - } - } - checkColsFuncInList(pFunc->pParameterList, pCheckouColsFuncCxt); - if (pCheckouColsFuncCxt->hasMultiColsFunc) { - return generateSyntaxErrMsgExt(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_COLS_FUNCTION, - "Invalid cols function in function %s", pFunc->functionName); - } - } else { - checkColsFunc(&pTmpNode, pCheckouColsFuncCxt); - if (pCheckouColsFuncCxt->hasMultiColsFunc) { - return generateSyntaxErrMsgExt(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_COLS_FUNCTION, - "Invalid cols function, can't be used at here"); - } - } - } - return TSDB_CODE_SUCCESS; -} - - - static int32_t getSelectFuncIndex(SNodeList* FuncNodeList, SNode* pSelectFunc) { SNode* pNode = NULL; int32_t selectFuncIndex = 0; @@ -7417,109 +7379,235 @@ static int32_t getSelectFuncIndex(SNodeList* FuncNodeList, SNode* pSelectFunc) { return 0; } -static int32_t rewriteColsFunction(STranslateContext* pCxt, SNodeList** nodeList) { - SCheckColsFuncCxt pCheckouColsFuncCxt = {false, false}; - int32_t code = hasInvalidColsFunction(pCxt, *nodeList, &pCheckouColsFuncCxt); - if (TSDB_CODE_SUCCESS != code) { - return code; +static EDealRes checkHasColsFunc(SNode** pNode, void* pContext){ + if (QUERY_NODE_FUNCTION == nodeType(*pNode)) { + SFunctionNode* pFunc = (SFunctionNode*)*pNode; + if (isColsFuncByName(pFunc)) { + *(bool*)pContext = true; + return DEAL_RES_END; + } } - bool needRewrite = false; - if (pCheckouColsFuncCxt.hasColsFunc) { - needRewrite = true; + return DEAL_RES_CONTINUE; +} + +static int32_t checkMultColsFuncParam(SNodeList* pParameterList) { + if (!pParameterList || pParameterList->length < 2) { + return TSDB_CODE_PAR_INVALID_COLS_FUNCTION; + } + int32_t index = 0; + SNode* pNode = NULL; + FOREACH(pNode, pParameterList) { + if (index == 0) { // the first parameter is select function + if (QUERY_NODE_FUNCTION != nodeType(pNode)) { + return TSDB_CODE_PAR_INVALID_COLS_FUNCTION; + } + SFunctionNode* pFunc = (SFunctionNode*)pNode; + // pFunc->funcId is zero at here, so need to check at * step + // if(!fmIsSelectFunc(pFunc->funcId)) { + // return TSDB_CODE_PAR_INVALID_COLS_FUNCTION; + // } + SNode* pTmpNode = NULL; + FOREACH(pTmpNode, pFunc->pParameterList) { + bool hasColsFunc = false; + nodesRewriteExpr(&pTmpNode, checkHasColsFunc, (void*)&hasColsFunc); + if (hasColsFunc) { + return TSDB_CODE_PAR_INVALID_COLS_FUNCTION; + } + } + } else { + bool hasColsFunc = false; + nodesRewriteExpr(&pNode, checkHasColsFunc, &hasColsFunc); + if (hasColsFunc) { + return TSDB_CODE_PAR_INVALID_COLS_FUNCTION; + } + } + ++index; + } + return TSDB_CODE_SUCCESS; +} + +static EDealRes rewriteSingleColsFunc(SNode** pNode, void* pContext) { + int32_t code = TSDB_CODE_SUCCESS; + if (QUERY_NODE_FUNCTION != nodeType(*pNode)) { + return DEAL_RES_CONTINUE; + } + SCheckColsFuncCxt* pCxt = pContext; + SFunctionNode* pFunc = (SFunctionNode*)*pNode; + if (isColsFuncByName(pFunc)) { + if(pFunc->pParameterList->length > 2) { + pCxt->status = TSDB_CODE_PAR_INVALID_COLS_SELECTFUNC; + return DEAL_RES_ERROR; + } + SNode* pSelectFunc = nodesListGetNode(pFunc->pParameterList, 0); + SNode* pExpr = nodesListGetNode(pFunc->pParameterList, 1); + if (nodeType(pSelectFunc) != QUERY_NODE_FUNCTION) { + pCxt->status = TSDB_CODE_PAR_INVALID_COLS_SELECTFUNC; + parserError("Invalid cols function, the first parameter must be a select function"); + return DEAL_RES_ERROR; + } + if (pFunc->node.asAlias) { + if (((SExprNode*)pExpr)->asAlias) { + pCxt->status = TSDB_CODE_INVALID_COLS_ALIAS; + parserError("Invalid using alias for cols function"); + return DEAL_RES_ERROR; + } else { + ((SExprNode*)pExpr)->asAlias = true; + tstrncpy(((SExprNode*)pExpr)->userAlias, pFunc->node.userAlias, TSDB_COL_NAME_LEN + TSDB_COL_NAME_EXLEN); + } + } + if(*pCxt->selectFuncList == NULL) { + nodesMakeList(pCxt->selectFuncList); + if (NULL == *pCxt->selectFuncList) { + pCxt->status = terrno; + return DEAL_RES_ERROR; + } + } + int32_t selectFuncCount = (*pCxt->selectFuncList)->length; + int32_t selectFuncIndex = getSelectFuncIndex(*pCxt->selectFuncList, pSelectFunc); + if (selectFuncIndex == 0) { + ++selectFuncCount; + selectFuncIndex = selectFuncCount; + SNode* pNewNode = NULL; + code = nodesCloneNode(pSelectFunc, &pNewNode); + ((SExprNode*)pNewNode)->tupleFuncIdx = selectFuncIndex; + nodesListMakeStrictAppend(pCxt->selectFuncList, pNewNode); + } + + SNode* pNewNode = NULL; + code = nodesCloneNode(pExpr, &pNewNode); + if (nodesIsExprNode(pNewNode)) { + SBindTupleFuncCxt pCxt = {selectFuncIndex}; + nodesRewriteExpr(&pNewNode, pushDownBindSelectFunc, &pCxt); + } else { + pCxt->status = TSDB_CODE_PAR_INVALID_COLS_FUNCTION; + parserError("Invalid cols function, the first parameter must be a select function"); + return DEAL_RES_ERROR; + } + nodesDestroyNode(*pNode); + *pNode = pNewNode; + } + return DEAL_RES_CONTINUE; +_end: + return code; +} + +static int32_t rewriteColsFunction(STranslateContext* pCxt, SNodeList** nodeList, SNodeList** selectFuncList) { + int32_t code = TSDB_CODE_SUCCESS; + bool needRewrite = false; + SNode** pNode = NULL; + FOREACH_FOR_REWRITE(pNode, *nodeList) { + if (isMultiColsFuncNode(*pNode)) { + code = checkMultColsFuncParam(((SFunctionNode*)*pNode)->pParameterList); + if (TSDB_CODE_SUCCESS != code) { + return code; + } + needRewrite = true; + } else { + SCheckColsFuncCxt pSelectFuncCxt = {false, selectFuncList, TSDB_CODE_SUCCESS}; + nodesRewriteExpr(pNode, rewriteSingleColsFunc, &pSelectFuncCxt); + if (pSelectFuncCxt.status != TSDB_CODE_SUCCESS) { + return pSelectFuncCxt.status; + } + } } SNodeList* pNewNodeList = NULL; - SNodeList* tmpFuncNodeList = NULL; if (needRewrite) { code = nodesMakeList(&pNewNodeList); if (NULL == pNewNodeList) { return code; } - code = nodesMakeList(&tmpFuncNodeList); - if (NULL == tmpFuncNodeList) { - return code; + if (*selectFuncList == NULL) { + code = nodesMakeList(selectFuncList); + if (NULL == *selectFuncList) { + nodesDestroyList(pNewNodeList); + return code; + } } - SNode* pNewNode = NULL; + + SNode* pNewNode = NULL; int32_t nums = 0; - int32_t selectFuncCount = 0; + int32_t selectFuncCount = (*selectFuncList)->length; SNode* pTmpNode = NULL; FOREACH(pTmpNode, *nodeList) { - if (QUERY_NODE_FUNCTION == nodeType(pTmpNode)) { + if (isMultiColsFuncNode(pTmpNode)) { SFunctionNode* pFunc = (SFunctionNode*)pTmpNode; - if (strcasecmp(pFunc->functionName, "cols") == 0) { - SNode* pSelectFunc = nodesListGetNode(pFunc->pParameterList, 0); - if (nodeType(pSelectFunc) != QUERY_NODE_FUNCTION) { + if(pFunc->node.asAlias) { + code = TSDB_CODE_INVALID_COLS_ALIAS; + parserError("Invalid using alias for cols function"); + goto _end; + } + + SNode* pSelectFunc = nodesListGetNode(pFunc->pParameterList, 0); + if (nodeType(pSelectFunc) != QUERY_NODE_FUNCTION) { + code = TSDB_CODE_PAR_INVALID_COLS_FUNCTION; + parserError("Invalid cols function, the first parameter must be a select function"); + goto _end; + } + int32_t selectFuncIndex = getSelectFuncIndex(*selectFuncList, pSelectFunc); + if (selectFuncIndex == 0) { + ++selectFuncCount; + selectFuncIndex = selectFuncCount; + code = nodesCloneNode(pSelectFunc, &pNewNode); + ((SExprNode*)pNewNode)->tupleFuncIdx = selectFuncIndex; + nodesListMakeStrictAppend(selectFuncList, pNewNode); + } + // start from index 1, because the first parameter is select function which needn't to output. + for (int i = 1; i < pFunc->pParameterList->length; ++i) { + SNode* pExpr = nodesListGetNode(pFunc->pParameterList, i); + + code = nodesCloneNode(pExpr, &pNewNode); + if (nodesIsExprNode(pNewNode)) { + SBindTupleFuncCxt pCxt = {selectFuncIndex}; + nodesRewriteExpr(&pNewNode, pushDownBindSelectFunc, &pCxt); + } else { code = TSDB_CODE_PAR_INVALID_COLS_FUNCTION; parserError("Invalid cols function, the first parameter must be a select function"); goto _end; } - int32_t selectFuncIndex = getSelectFuncIndex(tmpFuncNodeList, pSelectFunc); - if (selectFuncIndex == 0) { - ++selectFuncCount; - selectFuncIndex = selectFuncCount; - nodesListMakeStrictAppend(&tmpFuncNodeList, pSelectFunc); - } - // start from index 1, because the first parameter is select function which needn't to output. - for (int i = 1; i < pFunc->pParameterList->length; ++i) { - SNode* pExpr = nodesListGetNode(pFunc->pParameterList, i); - - code = nodesCloneNode(pExpr, &pNewNode); - if (nodesIsExprNode(pNewNode)) { - SBindTupleFuncCxt pCxt = {selectFuncIndex}; - nodesRewriteExpr(&pNewNode, pushDownBindSelectFunc, &pCxt); - } else { - code = TSDB_CODE_PAR_INVALID_COLS_FUNCTION; - parserError("Invalid cols function, the first parameter must be a select function"); - goto _end; - } - if (TSDB_CODE_SUCCESS != code) goto _end; - code = nodesListMakeStrictAppend(&pNewNodeList, pNewNode); - if (TSDB_CODE_SUCCESS != code) goto _end; - } - continue; + if (TSDB_CODE_SUCCESS != code) goto _end; + code = nodesListMakeStrictAppend(&pNewNodeList, pNewNode); + if (TSDB_CODE_SUCCESS != code) goto _end; } + continue; } + code = nodesCloneNode(pTmpNode, &pNewNode); if (TSDB_CODE_SUCCESS != code) goto _end; code = nodesListMakeStrictAppend(&pNewNodeList, pNewNode); if (TSDB_CODE_SUCCESS != code) goto _end; } - SNode* pNode = NULL; - int32_t pNewSelectFuncIds = 0; - FOREACH(pNode, tmpFuncNodeList) { - ++pNewSelectFuncIds; - code = nodesCloneNode(pNode, &pNewNode); - if (TSDB_CODE_SUCCESS != code) goto _end; - if (nodesIsExprNode(pNewNode)) { - SExprNode* pExprNode = (SExprNode*)pNewNode; - pExprNode->tupleFuncIdx = pNewSelectFuncIds; - } else { - code = TSDB_CODE_PAR_INVALID_COLS_FUNCTION; - parserError("Invalid cols function, the first parameter must be a select function"); - goto _end; - } - code = nodesListMakeStrictAppend(&pNewNodeList, pNewNode); - if (TSDB_CODE_SUCCESS != code) goto _end; - } nodesDestroyList(*nodeList); - nodesDestroyList(tmpFuncNodeList); *nodeList = pNewNodeList; + return TSDB_CODE_SUCCESS; } - return TSDB_CODE_SUCCESS; - -_end: - if (TSDB_CODE_SUCCESS != code) { - nodesDestroyList(pNewNodeList); - nodesDestroyList(tmpFuncNodeList); - } - return code; + _end: + if (TSDB_CODE_SUCCESS != code) { + nodesDestroyList(pNewNodeList); + } + return code; } static int32_t translateColsFunction(STranslateContext* pCxt, SSelectStmt* pSelect) { - int32_t code = rewriteColsFunction(pCxt, &pSelect->pProjectionList); + SNodeList* selectFuncList = NULL; + int32_t code = rewriteColsFunction(pCxt, &pSelect->pProjectionList, &selectFuncList); if (code == TSDB_CODE_SUCCESS) { - code = rewriteColsFunction(pCxt, &pSelect->pOrderByList); + code = rewriteColsFunction(pCxt, &pSelect->pOrderByList, &selectFuncList); } + if (TSDB_CODE_SUCCESS != code) { + goto _end; + } + + if (selectFuncList != NULL) { + nodesListAppendList(pSelect->pProjectionList, selectFuncList); + selectFuncList = NULL; + } + +_end: + if (selectFuncList) { + nodesDestroyList(selectFuncList); + } + return code; } diff --git a/source/util/src/terror.c b/source/util/src/terror.c index 2c32339efc..387d153b5a 100644 --- a/source/util/src/terror.c +++ b/source/util/src/terror.c @@ -751,6 +751,10 @@ TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_FORECAST_CLAUSE, "Invalid forecast cl TAOS_DEFINE_ERROR(TSDB_CODE_PAR_REGULAR_EXPRESSION_ERROR, "Syntax error in regular expression") TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_VGID_LIST, "Invalid vgid list") TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_COLS_FUNCTION, "Invalid cols function") +TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_COLS_SELECTFUNC, "cols function's first param must be a select function") +TAOS_DEFINE_ERROR(TSDB_CODE_INVALID_MULITI_COLS_FUNC, "Improper use of cols function with multiple output columns") +TAOS_DEFINE_ERROR(TSDB_CODE_INVALID_COLS_ALIAS, "Invalid using alias for cols function") + TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INTERNAL_ERROR, "Parser internal error") //planner diff --git a/tests/system-test/2-query/cols_function.py b/tests/system-test/2-query/cols_function.py index 5f48c401db..f049f52480 100644 --- a/tests/system-test/2-query/cols_function.py +++ b/tests/system-test/2-query/cols_function.py @@ -233,6 +233,7 @@ class TDTestCase: tdSql.error(f'select cols(last(ts), ts t1) tt from {self.dbname}.meters') tdSql.error(f'select cols(first(ts+1), c0+2 cc0, c1 cc1) cc from {self.dbname}.meters') tdSql.error(f'select cols(last(ts)+1, c0+2 as cc0) as cc from {self.dbname}.meters') + tdSql.error(f'select cols(ABS(c0), c1) from {self.dbname}.meters group by tbname') tdSql.error(f'select cols(last(ts)+1, ts) from {self.dbname}.meters') tdSql.error(f'select cols(last(ts)+10, c1+10) from {self.dbname}.meters group by tbname')