diff --git a/docs/en/14-reference/09-error-code.md b/docs/en/14-reference/09-error-code.md index 233ac78a198f..c35df8ebc01e 100644 --- a/docs/en/14-reference/09-error-code.md +++ b/docs/en/14-reference/09-error-code.md @@ -458,6 +458,10 @@ This document details the server error codes that may be encountered when using | 0x80002665 | The _TAGS pseudocolumn can only be used for subtable and supertable queries | Illegal tag column query | Check and correct the SQL statement | | 0x80002666 | Subquery does not output primary timestamp column | Check and correct the SQL statement | | | 0x80002667 | Invalid usage of expr: %s | Illegal expression | Check and correct the SQL statement | +| 0x80002687 | Invalid using cols function | Illegal using cols function | Check and correct the SQL statement | +| 0x80002688 | Cols function's first param must be a select function | The first parameter of the cols function should be a selection function | Check and correct the SQL statement | +| 0x80002689 | Invalid using cols function with multiple output columns | Illegal using the cols function for multiple column output | Check and correct the SQL statement | +| 0x80002690 | Invalid using alias for cols function | Illegal cols function alias | Check and correct the SQL statement | | 0x800026FF | Parser internal error | Internal error in parser | Preserve the scene and logs, report issue on GitHub | | 0x80002700 | Planner internal error | Internal error in planner | Preserve the scene and logs, report issue on GitHub | | 0x80002701 | Expect ts equal | JOIN condition validation failed | Preserve the scene and logs, report issue on GitHub | diff --git a/docs/zh/14-reference/09-error-code.md b/docs/zh/14-reference/09-error-code.md index d29ff542eb09..a1a3fa050de4 100644 --- a/docs/zh/14-reference/09-error-code.md +++ b/docs/zh/14-reference/09-error-code.md @@ -473,8 +473,12 @@ description: TDengine 服务端的错误码列表和详细说明 | 0x80002663 | Not unique table/alias | 表名(别名)冲突 | 检查并修正SQL语句 | | 0x80002664 | Join requires valid time series input | 不支持子查询不含主键时间戳列输出的JOIN查询 | 检查并修正SQL语句 | | 0x80002665 | The _TAGS pseudo column can only be used for subtable and supertable queries | 非法TAG列查询 | 检查并修正SQL语句 | -| 0x80002666 | 子查询不含主键时间戳列输出 | 检查并修正SQL语句 | +| 0x80002666 | 子查询不含主键时间戳列输出 | 检查并修正SQL语句 | | 0x80002667 | Invalid usage of expr: %s | 非法表达式 | 检查并修正SQL语句 | +| 0x80002687 | Invalid using cols function | cols函数使用错误 | 检查并修正SQL语句 | +| 0x80002688 | Cols function's first param must be a select function | cols函数第一个参数应该为选择函数 | 检查并修正SQL语句 | +| 0x80002689 | Invalid using cols function with multiple output columns | 多列输出的 cols 函数使用错误 | 检查并修正SQL语句 | +| 0x80002690 | Invalid using alias for cols function | cols 函数输出列重命名错误 | 检查并修正SQL语句 | | 0x800026FF | Parser internal error | 解析器内部错误 | 保留现场和日志,github上报issue | | 0x80002700 | Planner internal error | 计划期内部错误 | 保留现场和日志,github上报issue | | 0x80002701 | Expect ts equal | JOIN条件校验失败 | 保留现场和日志,github上报issue | diff --git a/include/libs/function/function.h b/include/libs/function/function.h index 126ed2c9b054..715f25768840 100644 --- a/include/libs/function/function.h +++ b/include/libs/function/function.h @@ -282,6 +282,8 @@ typedef struct tExprNode { struct SNode *pRootNode; } _optrRoot; }; + int32_t bindTupleFuncIdx; + int32_t tupleFuncIdx; } tExprNode; struct SScalarParam { diff --git a/include/libs/function/functionMgt.h b/include/libs/function/functionMgt.h index 853f70e755e5..41b5d76371f7 100644 --- a/include/libs/function/functionMgt.h +++ b/include/libs/function/functionMgt.h @@ -155,6 +155,7 @@ typedef enum EFunctionType { FUNCTION_TYPE_FORECAST_LOW, FUNCTION_TYPE_FORECAST_HIGH, FUNCTION_TYPE_FORECAST_ROWTS, + FUNCTION_TYPE_COLS, FUNCTION_TYPE_IROWTS_ORIGIN, // internal function @@ -208,6 +209,7 @@ typedef enum EFunctionType { FUNCTION_TYPE_HYPERLOGLOG_STATE, FUNCTION_TYPE_HYPERLOGLOG_STATE_MERGE, + // geometry functions FUNCTION_TYPE_GEOM_FROM_TEXT = 4250, FUNCTION_TYPE_AS_TEXT, @@ -295,6 +297,7 @@ bool fmisSelectGroupConstValueFunc(int32_t funcId); bool fmIsElapsedFunc(int32_t funcId); bool fmIsDBUsageFunc(int32_t funcId); bool fmIsRowTsOriginFunc(int32_t funcId); +bool fmIsSelectColsFunc(int32_t funcId); void getLastCacheDataType(SDataType* pType, int32_t pkBytes); int32_t createFunction(const char* pName, SNodeList* pParameterList, SFunctionNode** pFunc); diff --git a/include/libs/nodes/querynodes.h b/include/libs/nodes/querynodes.h index 5e4e8b62926a..653b5ff6cddc 100644 --- a/include/libs/nodes/querynodes.h +++ b/include/libs/nodes/querynodes.h @@ -16,6 +16,7 @@ #ifndef _TD_QUERY_NODES_H_ #define _TD_QUERY_NODES_H_ +#include #ifdef __cplusplus extern "C" { #endif @@ -61,6 +62,8 @@ typedef struct SExprNode { bool asParam; bool asPosition; int32_t projIdx; + int32_t bindTupleFuncIdx; + int32_t tupleFuncIdx; } SExprNode; typedef enum EColumnType { @@ -696,6 +699,8 @@ char* getJoinSTypeString(EJoinSubType type); char* getFullJoinTypeString(EJoinType type, EJoinSubType stype); int32_t mergeJoinConds(SNode** ppDst, SNode** ppSrc); +int32_t rewriteExprAliasName(SExprNode* pNode, int64_t num); + #ifdef __cplusplus } #endif diff --git a/include/util/taoserror.h b/include/util/taoserror.h index 464dffa937ba..5c83aa90b263 100644 --- a/include/util/taoserror.h +++ b/include/util/taoserror.h @@ -908,6 +908,10 @@ int32_t taosGetErrSize(); #define TSDB_CODE_PAR_INVALID_ANOMALY_WIN_OPT TAOS_DEF_ERROR_CODE(0, 0x2684) #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, 0x2689) +#define TSDB_CODE_INVALID_COLS_ALIAS TAOS_DEF_ERROR_CODE(0, 0x2690) #define TSDB_CODE_PAR_INTERNAL_ERROR TAOS_DEF_ERROR_CODE(0, 0x26FF) //planner diff --git a/include/util/tdef.h b/include/util/tdef.h index f08697b0d424..1569e9eee34a 100644 --- a/include/util/tdef.h +++ b/include/util/tdef.h @@ -272,6 +272,7 @@ typedef enum ELogicConditionType { #define TSDB_SUBSCRIBE_KEY_LEN (TSDB_CGROUP_LEN + TSDB_TOPIC_FNAME_LEN + 2) #define TSDB_PARTITION_KEY_LEN (TSDB_SUBSCRIBE_KEY_LEN + 20) #define TSDB_COL_NAME_LEN 65 +#define TSDB_COL_NAME_EXLEN 4 #define TSDB_COL_FNAME_LEN (TSDB_TABLE_NAME_LEN + TSDB_COL_NAME_LEN + TSDB_NAME_DELIMITER_LEN) #define TSDB_MAX_SAVED_SQL_LEN TSDB_MAX_COLUMNS * 64 #define TSDB_MAX_SQL_LEN TSDB_PAYLOAD_SIZE diff --git a/source/dnode/mnode/impl/src/mndDump.c b/source/dnode/mnode/impl/src/mndDump.c index d1dd99b31969..485a89f1bd97 100644 --- a/source/dnode/mnode/impl/src/mndDump.c +++ b/source/dnode/mnode/impl/src/mndDump.c @@ -41,7 +41,7 @@ int32_t sendSyncReq(const SEpSet *pEpSet, SRpcMsg *pMsg) { } char *i642str(int64_t val) { - static char str[24] = {0}; + static threadlocal char str[24] = {0}; (void)snprintf(str, sizeof(str), "%" PRId64, val); return str; } diff --git a/source/libs/executor/src/aggregateoperator.c b/source/libs/executor/src/aggregateoperator.c index 571372650187..bec8bf7f6fc3 100644 --- a/source/libs/executor/src/aggregateoperator.c +++ b/source/libs/executor/src/aggregateoperator.c @@ -395,6 +395,12 @@ static int32_t createDataBlockForEmptyInput(SOperatorInfo* pOperator, SSDataBloc return TSDB_CODE_SUCCESS; } + // if the last expression is a tuple function, we don't need to create a empty data block + int32_t lastExprIndex = pOperator->exprSupp.numOfExprs - 1; + if(pOperator->exprSupp.pExprInfo[lastExprIndex].pExpr->tupleFuncIdx > 0) { + return TSDB_CODE_SUCCESS; + } + code = createDataBlock(&pBlock); if (code) { return code; diff --git a/source/libs/executor/src/executil.c b/source/libs/executor/src/executil.c index cce754a8c803..c57accfa663c 100644 --- a/source/libs/executor/src/executil.c +++ b/source/libs/executor/src/executil.c @@ -18,6 +18,8 @@ #include "index.h" #include "os.h" #include "query.h" +#include "querynodes.h" +#include "tarray.h" #include "tdatablock.h" #include "thash.h" #include "tmsg.h" @@ -1990,7 +1992,8 @@ int32_t createExprFromOneNode(SExprInfo* pExp, SNode* pNode, int16_t slotId) { code = TSDB_CODE_QRY_EXECUTOR_INTERNAL_ERROR; QUERY_CHECK_CODE(code, lino, _end); } - + pExp->pExpr->bindTupleFuncIdx = ((SExprNode*)pNode)->bindTupleFuncIdx; + pExp->pExpr->tupleFuncIdx = ((SExprNode*)pNode)->tupleFuncIdx; _end: if (code != TSDB_CODE_SUCCESS) { qError("%s failed at line %d since %s", __func__, lino, tstrerror(code)); @@ -2071,42 +2074,81 @@ int32_t createExprInfo(SNodeList* pNodeList, SNodeList* pGroupKeys, SExprInfo** return code; } +static void deleteSubsidiareCtx(void* pData) { + SSubsidiaryResInfo* pCtx = (SSubsidiaryResInfo*)pData; + if (pCtx->pCtx) { + taosMemoryFreeClear(pCtx->pCtx); + } +} + // set the output buffer for the selectivity + tag query static int32_t setSelectValueColumnInfo(SqlFunctionCtx* pCtx, int32_t numOfOutput) { int32_t num = 0; int32_t code = TSDB_CODE_SUCCESS; int32_t lino = 0; - SqlFunctionCtx* p = NULL; - SqlFunctionCtx** pValCtx = taosMemoryCalloc(numOfOutput, POINTER_BYTES); - if (pValCtx == NULL) { - return terrno; + SArray* pValCtxArray = NULL; + for (int32_t i = numOfOutput - 1; i > 0; --i) { // select Func is at the end of the list + int32_t funcIdx = pCtx[i].pExpr->pExpr->tupleFuncIdx; + if (funcIdx > 0) { + if (pValCtxArray == NULL) { + // the end of the list is the select function of biggest index + pValCtxArray = taosArrayInit_s(sizeof(SSubsidiaryResInfo*), funcIdx); + if (pValCtxArray == NULL) { + return terrno; + } + } + if (funcIdx > pValCtxArray->size) { + qError("funcIdx:%d is out of range", funcIdx); + taosArrayDestroyP(pValCtxArray, deleteSubsidiareCtx); + return TSDB_CODE_QRY_EXECUTOR_INTERNAL_ERROR; + } + SSubsidiaryResInfo* pSubsidiary = &pCtx[i].subsidiaries; + pSubsidiary->pCtx = taosMemoryCalloc(numOfOutput, POINTER_BYTES); + if (pSubsidiary->pCtx == NULL) { + taosArrayDestroyP(pValCtxArray, deleteSubsidiareCtx); + return terrno; + } + pSubsidiary->num = 0; + taosArraySet(pValCtxArray, funcIdx - 1, &pSubsidiary); + } else { + break; + } } - SHashObj* pSelectFuncs = taosHashInit(8, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), false, HASH_ENTRY_LOCK); - QUERY_CHECK_NULL(pSelectFuncs, code, lino, _end, terrno); + SqlFunctionCtx* p = NULL; + SqlFunctionCtx** pValCtx = NULL; + if (pValCtxArray == NULL) { + pValCtx = taosMemoryCalloc(numOfOutput, POINTER_BYTES); + if (pValCtx == NULL) { + QUERY_CHECK_CODE(terrno, lino, _end); + } + } for (int32_t i = 0; i < numOfOutput; ++i) { const char* pName = pCtx[i].pExpr->pExpr->_function.functionName; if ((strcmp(pName, "_select_value") == 0) || (strcmp(pName, "_group_key") == 0) || (strcmp(pName, "_group_const_value") == 0)) { - pValCtx[num++] = &pCtx[i]; - } else if (fmIsSelectFunc(pCtx[i].functionId)) { - void* data = taosHashGet(pSelectFuncs, pName, strlen(pName)); - if (taosHashGetSize(pSelectFuncs) != 0 && data == NULL) { - p = NULL; - break; + if (pValCtxArray == NULL) { + pValCtx[num++] = &pCtx[i]; } else { - int32_t tempRes = taosHashPut(pSelectFuncs, pName, strlen(pName), &num, sizeof(num)); - if (tempRes != TSDB_CODE_SUCCESS && tempRes != TSDB_CODE_DUP_KEY) { - code = tempRes; - QUERY_CHECK_CODE(code, lino, _end); + int32_t bindFuncIndex = pCtx[i].pExpr->pExpr->bindTupleFuncIdx; // start from index 1; + if (bindFuncIndex > 0) { // 0 is default index related to the select function + bindFuncIndex -= 1; } - p = &pCtx[i]; + SSubsidiaryResInfo** pSubsidiary = taosArrayGet(pValCtxArray, bindFuncIndex); + if(pSubsidiary == NULL) { + QUERY_CHECK_CODE(TSDB_CODE_QRY_EXECUTOR_INTERNAL_ERROR, lino, _end); + } + (*pSubsidiary)->pCtx[(*pSubsidiary)->num] = &pCtx[i]; + (*pSubsidiary)->num++; } + } else if (fmIsSelectFunc(pCtx[i].functionId)) { + if (pValCtxArray == NULL) { + p = &pCtx[i]; + } } } - taosHashCleanup(pSelectFuncs); if (p != NULL) { p->subsidiaries.pCtx = pValCtx; @@ -2117,9 +2159,11 @@ static int32_t setSelectValueColumnInfo(SqlFunctionCtx* pCtx, int32_t numOfOutpu _end: if (code != TSDB_CODE_SUCCESS) { + taosArrayDestroyP(pValCtxArray, deleteSubsidiareCtx); taosMemoryFreeClear(pValCtx); - taosHashCleanup(pSelectFuncs); qError("%s failed at line %d since %s", __func__, lino, tstrerror(code)); + } else { + taosArrayDestroy(pValCtxArray); } return code; } diff --git a/source/libs/function/inc/functionMgtInt.h b/source/libs/function/inc/functionMgtInt.h index e10581beb632..d676d4b72867 100644 --- a/source/libs/function/inc/functionMgtInt.h +++ b/source/libs/function/inc/functionMgtInt.h @@ -59,6 +59,7 @@ extern "C" { #define FUNC_MGT_COUNT_LIKE_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(30) // funcs that should also return 0 when no rows found #define FUNC_MGT_PROCESS_BY_ROW FUNC_MGT_FUNC_CLASSIFICATION_MASK(31) #define FUNC_MGT_FORECAST_PC_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(32) +#define FUNC_MGT_SELECT_COLS_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(33) #define FUNC_MGT_TEST_MASK(val, mask) (((val) & (mask)) != 0) diff --git a/source/libs/function/src/builtins.c b/source/libs/function/src/builtins.c index b42d739b4066..722f838061b1 100644 --- a/source/libs/function/src/builtins.c +++ b/source/libs/function/src/builtins.c @@ -1702,6 +1702,10 @@ static int32_t translateOutVarchar(SFunctionNode* pFunc, char* pErrBuf, int32_t return TSDB_CODE_SUCCESS; } +static int32_t invalidColsFunction(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); @@ -2737,7 +2741,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = { .translateFunc = translateOutFirstIn, .dynDataRequiredFunc = firstDynDataReq, .getEnvFunc = getFirstLastFuncEnv, - .initFunc = functionSetup, + .initFunc = firstLastFunctionSetup, .processFunc = firstFunction, .sprocessFunc = firstLastScalarFunction, .finalizeFunc = firstLastFinalize, @@ -5647,7 +5651,11 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = { .paramInfoPattern = 0, .outputParaInfo = {.validDataType = FUNC_PARAM_SUPPORT_VARCHAR_TYPE}}, .translateFunc = translateOutVarchar, - } + }, + { + .name = "cols", + .translateFunc = invalidColsFunction, + }, }; // clang-format on diff --git a/source/libs/function/src/builtinsimpl.c b/source/libs/function/src/builtinsimpl.c index efe16ce662ea..8618c23a799b 100644 --- a/source/libs/function/src/builtinsimpl.c +++ b/source/libs/function/src/builtinsimpl.c @@ -974,7 +974,7 @@ int32_t setSelectivityValue(SqlFunctionCtx* pCtx, SSDataBlock* pBlock, const STu if (nullList[j]) { colDataSetNULL(pDstCol, rowIndex); } else { - code = colDataSetVal(pDstCol, rowIndex, pStart, false); + code = colDataSetValOrCover(pDstCol, rowIndex, pStart, false); if (TSDB_CODE_SUCCESS != code) { return code; } @@ -2431,7 +2431,6 @@ int32_t firstLastFunctionSetup(SqlFunctionCtx* pCtx, SResultRowEntryInfo* pResIn } SFirstLastRes* pRes = GET_ROWCELL_INTERBUF(pResInfo); - SInputColumnInfoData* pInput = &pCtx->input; pRes->nullTupleSaved = false; pRes->nullTuplePos.pageId = -1; @@ -2768,7 +2767,7 @@ int32_t lastFunction(SqlFunctionCtx* pCtx) { if (pResInfo->numOfRes == 0 || pInfo->ts < cts) { char* data = colDataGetData(pInputCol, chosen); - int32_t code = doSaveCurrentVal(pCtx, i, cts, NULL, type, data); + int32_t code = doSaveCurrentVal(pCtx, chosen, cts, NULL, type, data); if (code != TSDB_CODE_SUCCESS) { return code; } diff --git a/source/libs/function/src/functionMgt.c b/source/libs/function/src/functionMgt.c index 4fb3aab49de7..a88bee48fa7b 100644 --- a/source/libs/function/src/functionMgt.c +++ b/source/libs/function/src/functionMgt.c @@ -173,6 +173,8 @@ bool fmIsScalarFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC bool fmIsVectorFunc(int32_t funcId) { return !fmIsScalarFunc(funcId) && !fmIsPseudoColumnFunc(funcId); } +bool fmIsSelectColsFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_SELECT_COLS_FUNC); } + bool fmIsSelectFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_SELECT_FUNC); } bool fmIsTimelineFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_TIMELINE_FUNC); } @@ -441,6 +443,8 @@ int32_t createFunctionWithSrcFunc(const char* pName, const SFunctionNode* pSrcFu return code; } resetOutputChangedFunc(*ppFunc, pSrcFunc); + (*ppFunc)->node.bindTupleFuncIdx = pSrcFunc->node.bindTupleFuncIdx; + (*ppFunc)->node.tupleFuncIdx = pSrcFunc->node.tupleFuncIdx; return code; } diff --git a/source/libs/nodes/src/nodesCloneFuncs.c b/source/libs/nodes/src/nodesCloneFuncs.c index 6d245ebd611a..11c9b322d2f7 100644 --- a/source/libs/nodes/src/nodesCloneFuncs.c +++ b/source/libs/nodes/src/nodesCloneFuncs.c @@ -106,6 +106,8 @@ static int32_t exprNodeCopy(const SExprNode* pSrc, SExprNode* pDst) { COPY_SCALAR_FIELD(asParam); COPY_SCALAR_FIELD(asPosition); COPY_SCALAR_FIELD(projIdx); + COPY_SCALAR_FIELD(bindTupleFuncIdx); + COPY_SCALAR_FIELD(tupleFuncIdx); return TSDB_CODE_SUCCESS; } diff --git a/source/libs/nodes/src/nodesEqualFuncs.c b/source/libs/nodes/src/nodesEqualFuncs.c index 891843761a03..d255c2fd6b6f 100644 --- a/source/libs/nodes/src/nodesEqualFuncs.c +++ b/source/libs/nodes/src/nodesEqualFuncs.c @@ -13,6 +13,7 @@ * along with this program. If not, see . */ +#include "functionMgt.h" #include "querynodes.h" #define COMPARE_SCALAR_FIELD(fldname) \ @@ -137,6 +138,14 @@ static bool functionNodeEqual(const SFunctionNode* a, const SFunctionNode* b) { COMPARE_SCALAR_FIELD(funcId); COMPARE_STRING_FIELD(functionName); COMPARE_NODE_LIST_FIELD(pParameterList); + if (a->funcType == FUNCTION_TYPE_SELECT_VALUE) { + if ((a->node.bindTupleFuncIdx != b->node.bindTupleFuncIdx)) return false; + } else { + if ((a->node.tupleFuncIdx != b->node.tupleFuncIdx)) { + return false; + } + } + return true; } diff --git a/source/libs/nodes/src/nodesMsgFuncs.c b/source/libs/nodes/src/nodesMsgFuncs.c index 930a88aea089..5bd773796fd6 100644 --- a/source/libs/nodes/src/nodesMsgFuncs.c +++ b/source/libs/nodes/src/nodesMsgFuncs.c @@ -664,11 +664,18 @@ static int32_t msgToDataType(STlvDecoder* pDecoder, void* pObj) { return code; } -enum { EXPR_CODE_RES_TYPE = 1 }; +enum { EXPR_CODE_RES_TYPE = 1, EXPR_CODE_BIND_TUPLE_FUNC_IDX, EXPR_CODE_TUPLE_FUNC_IDX }; static int32_t exprNodeToMsg(const void* pObj, STlvEncoder* pEncoder) { const SExprNode* pNode = (const SExprNode*)pObj; - return tlvEncodeObj(pEncoder, EXPR_CODE_RES_TYPE, dataTypeToMsg, &pNode->resType); + int32_t code = tlvEncodeObj(pEncoder, EXPR_CODE_RES_TYPE, dataTypeToMsg, &pNode->resType); + if (TSDB_CODE_SUCCESS == code) { + code = tlvEncodeI32(pEncoder, EXPR_CODE_BIND_TUPLE_FUNC_IDX, pNode->bindTupleFuncIdx); + } + if (TSDB_CODE_SUCCESS == code) { + code = tlvEncodeI32(pEncoder, EXPR_CODE_TUPLE_FUNC_IDX, pNode->tupleFuncIdx); + } + return code; } static int32_t msgToExprNode(STlvDecoder* pDecoder, void* pObj) { @@ -681,6 +688,12 @@ static int32_t msgToExprNode(STlvDecoder* pDecoder, void* pObj) { case EXPR_CODE_RES_TYPE: code = tlvDecodeObjFromTlv(pTlv, msgToDataType, &pNode->resType); break; + case EXPR_CODE_BIND_TUPLE_FUNC_IDX: + code = tlvDecodeI32(pTlv, &pNode->bindTupleFuncIdx); + break; + case EXPR_CODE_TUPLE_FUNC_IDX: + code = tlvDecodeI32(pTlv, &pNode->tupleFuncIdx); + break; default: break; } @@ -695,6 +708,12 @@ static int32_t columnNodeInlineToMsg(const void* pObj, STlvEncoder* pEncoder) { const SColumnNode* pNode = (const SColumnNode*)pObj; int32_t code = dataTypeInlineToMsg(&pNode->node.resType, pEncoder); + if (TSDB_CODE_SUCCESS == code) { + code = tlvEncodeValueI32(pEncoder, pNode->node.bindTupleFuncIdx); + } + if (TSDB_CODE_SUCCESS == code) { + code = tlvEncodeValueI32(pEncoder, pNode->node.tupleFuncIdx); + } if (TSDB_CODE_SUCCESS == code) { code = tlvEncodeValueU64(pEncoder, pNode->tableId); } @@ -745,6 +764,12 @@ static int32_t msgToColumnNodeInline(STlvDecoder* pDecoder, void* pObj) { SColumnNode* pNode = (SColumnNode*)pObj; int32_t code = msgToDataTypeInline(pDecoder, &pNode->node.resType); + if (TSDB_CODE_SUCCESS == code) { + code = tlvDecodeValueI32(pDecoder, &pNode->node.bindTupleFuncIdx); + } + if (TSDB_CODE_SUCCESS == code) { + code = tlvDecodeValueI32(pDecoder, &pNode->node.tupleFuncIdx); + } if (TSDB_CODE_SUCCESS == code) { code = tlvDecodeValueU64(pDecoder, &pNode->tableId); } diff --git a/source/libs/nodes/src/nodesUtilFuncs.c b/source/libs/nodes/src/nodesUtilFuncs.c index ae5b302d2da0..dd4d1c694bd4 100644 --- a/source/libs/nodes/src/nodesUtilFuncs.c +++ b/source/libs/nodes/src/nodesUtilFuncs.c @@ -3237,3 +3237,7 @@ int32_t nodesListDeduplicate(SNodeList** ppList) { } return code; } + +int32_t rewriteExprAliasName(SExprNode* pNode, int64_t num) { + return tsnprintf(pNode->aliasName, TSDB_COL_NAME_LEN, "expr_%d", num); +} diff --git a/source/libs/parser/inc/parAst.h b/source/libs/parser/inc/parAst.h index 387bccf358fb..f2b1059a846d 100644 --- a/source/libs/parser/inc/parAst.h +++ b/source/libs/parser/inc/parAst.h @@ -170,6 +170,7 @@ SNode* createInterpTimeAround(SAstCreateContext* pCxt, SNode* pTimepoint, SN SNode* createWhenThenNode(SAstCreateContext* pCxt, SNode* pWhen, SNode* pThen); SNode* createCaseWhenNode(SAstCreateContext* pCxt, SNode* pCase, SNodeList* pWhenThenList, SNode* pElse); SNode* createAlterSingleTagColumnNode(SAstCreateContext* pCtx, SToken* token, SNode* pVal); +SNode* createColsFunctionNode(SAstCreateContext* pCxt, SNode* pFunc, SNodeList* pList); SNode* addWhereClause(SAstCreateContext* pCxt, SNode* pStmt, SNode* pWhere); SNode* addPartitionByClause(SAstCreateContext* pCxt, SNode* pStmt, SNodeList* pPartitionByList); @@ -335,6 +336,7 @@ SNode* createDropTSMAStmt(SAstCreateContext* pCxt, bool ignoreNotExists, SNode* SNode* createShowCreateTSMAStmt(SAstCreateContext* pCxt, SNode* pRealTable); SNode* createShowTSMASStmt(SAstCreateContext* pCxt, SNode* dbName); SNode* createShowDiskUsageStmt(SAstCreateContext* pCxt, SNode* dbName, ENodeType type); +SNodeList* createColsFuncParamNodeList(SAstCreateContext* pCxt, SNode* pFuncNode, SNodeList* pNodeList, SToken* pAlias); #ifdef __cplusplus } diff --git a/source/libs/parser/inc/sql.y b/source/libs/parser/inc/sql.y index 439af13d7130..5b6081b1fede 100644 --- a/source/libs/parser/inc/sql.y +++ b/source/libs/parser/inc/sql.y @@ -1276,6 +1276,7 @@ pseudo_column(A) ::= IROWTS_ORIGIN(B). function_expression(A) ::= function_name(B) NK_LP expression_list(C) NK_RP(D). { A = createRawExprNodeExt(pCxt, &B, &D, createFunctionNode(pCxt, &B, C)); } function_expression(A) ::= star_func(B) NK_LP star_func_para_list(C) NK_RP(D). { A = createRawExprNodeExt(pCxt, &B, &D, createFunctionNode(pCxt, &B, C)); } +function_expression(A) ::= cols_func(B) NK_LP cols_func_para_list(C) NK_RP(D). { A = createRawExprNodeExt(pCxt, &B, &D, createFunctionNode(pCxt, &B, C)); } function_expression(A) ::= CAST(B) NK_LP expr_or_subquery(C) AS type_name(D) NK_RP(E). { A = createRawExprNodeExt(pCxt, &B, &E, createCastFunctionNode(pCxt, releaseRawExprNode(pCxt, C), D)); } function_expression(A) ::= @@ -1338,6 +1339,23 @@ star_func(A) ::= FIRST(B). star_func(A) ::= LAST(B). { A = B; } star_func(A) ::= LAST_ROW(B). { A = B; } +%type cols_func { SToken } +%destructor cols_func { } +cols_func(A) ::= COLS(B). { A = B; } + +%type cols_func_para_list { SNodeList* } +%destructor cols_func_para_list { nodesDestroyList($$); } +cols_func_para_list(A) ::= function_expression(B) NK_COMMA cols_func_expression_list(C). { A = createColsFuncParamNodeList(pCxt, B, C, NULL); } + +cols_func_expression(A) ::= expr_or_subquery(B). { A = releaseRawExprNode(pCxt, B); } +cols_func_expression(A) ::= expr_or_subquery(B) column_alias(C). { A = setProjectionAlias(pCxt, releaseRawExprNode(pCxt, B), &C);} +cols_func_expression(A) ::= expr_or_subquery(B) AS column_alias(C). { A = setProjectionAlias(pCxt, releaseRawExprNode(pCxt, B), &C);} + +%type cols_func_expression_list { SNodeList* } +%destructor cols_func_expression_list { nodesDestroyList($$); } +cols_func_expression_list(A) ::= cols_func_expression(B). { A = createNodeList(pCxt, B); } +cols_func_expression_list(A) ::= cols_func_expression_list(B) NK_COMMA cols_func_expression(C). { A = addNodeToList(pCxt, B, C); } + %type star_func_para_list { SNodeList* } %destructor star_func_para_list { nodesDestroyList($$); } star_func_para_list(A) ::= NK_STAR(B). { A = createNodeList(pCxt, createColumnNode(pCxt, NULL, &B)); } diff --git a/source/libs/parser/src/parAstCreater.c b/source/libs/parser/src/parAstCreater.c index c875cbad0581..478d277d83dc 100644 --- a/source/libs/parser/src/parAstCreater.c +++ b/source/libs/parser/src/parAstCreater.c @@ -16,6 +16,7 @@ #include #include +#include "nodes.h" #include "parAst.h" #include "parUtil.h" #include "tglobal.h" @@ -356,6 +357,32 @@ SToken getTokenFromRawExprNode(SAstCreateContext* pCxt, SNode* pNode) { return t; } +SNodeList* createColsFuncParamNodeList(SAstCreateContext* pCxt, SNode* pNode, SNodeList* pNodeList, SToken* pAlias) { + CHECK_PARSER_STATUS(pCxt); + if (NULL == pNode || QUERY_NODE_RAW_EXPR != nodeType(pNode)) { + pCxt->errCode = TSDB_CODE_PAR_SYNTAX_ERROR; + } + CHECK_PARSER_STATUS(pCxt); + SRawExprNode* pRawExpr = (SRawExprNode*)pNode; + SNode* pFuncNode = pRawExpr->pNode; + if(pFuncNode->type != QUERY_NODE_FUNCTION) { + pCxt->errCode = TSDB_CODE_PAR_SYNTAX_ERROR; + } + CHECK_PARSER_STATUS(pCxt); + SNodeList* list = NULL; + pCxt->errCode = nodesMakeList(&list); + CHECK_MAKE_NODE(list); + pCxt->errCode = nodesListAppend(list, pFuncNode); + CHECK_PARSER_STATUS(pCxt); + pCxt->errCode = nodesListAppendList(list, pNodeList); + return list; + + _err: + nodesDestroyNode(pFuncNode); + nodesDestroyList(pNodeList); + return NULL; +} + SNodeList* createNodeList(SAstCreateContext* pCxt, SNode* pNode) { CHECK_PARSER_STATUS(pCxt); SNodeList* list = NULL; @@ -1150,6 +1177,24 @@ SNode* createSubstrFunctionNode(SAstCreateContext* pCxt, SNode* pExpr, SNode* pE return NULL; } +SNode* createColsFunctionNode(SAstCreateContext* pCxt, SNode* pColFunc, SNodeList* pExpr){ + SFunctionNode* func = NULL; + CHECK_PARSER_STATUS(pCxt); + pCxt->errCode = nodesMakeNode(QUERY_NODE_FUNCTION, (SNode**)&func); + CHECK_MAKE_NODE(func); + strcpy(func->functionName, "cols"); + pCxt->errCode = nodesListMakeAppend(&func->pParameterList, pColFunc); + CHECK_PARSER_STATUS(pCxt); + pCxt->errCode = nodesListMakeStrictAppendList(&func->pParameterList, pExpr); + CHECK_PARSER_STATUS(pCxt); + return (SNode*)func; +_err: + nodesDestroyNode((SNode*)func); + nodesDestroyNode(pColFunc); + nodesDestroyList(pExpr); + return NULL; +} + SNode* createSubstrFunctionNodeExt(SAstCreateContext* pCxt, SNode* pExpr, SNode* pExpr2, SNode* pExpr3) { SFunctionNode* func = NULL; CHECK_PARSER_STATUS(pCxt); diff --git a/source/libs/parser/src/parTokenizer.c b/source/libs/parser/src/parTokenizer.c index 7ed438a7dc81..7c4d8db0d3ed 100644 --- a/source/libs/parser/src/parTokenizer.c +++ b/source/libs/parser/src/parTokenizer.c @@ -355,6 +355,7 @@ static SKeyword keywordTable[] = { {"FORCE_WINDOW_CLOSE", TK_FORCE_WINDOW_CLOSE}, {"DISK_INFO", TK_DISK_INFO}, {"AUTO", TK_AUTO}, + {"COLS", TK_COLS}, {"NOTIFY", TK_NOTIFY}, {"ON_FAILURE", TK_ON_FAILURE}, {"NOTIFY_HISTORY", TK_NOTIFY_HISTORY}, diff --git a/source/libs/parser/src/parTranslater.c b/source/libs/parser/src/parTranslater.c index 74dd1be6147f..2842ea2d42ef 100755 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -13,8 +13,13 @@ * along with this program. If not, see . */ +#include "nodes.h" #include "parInt.h" #include "parTranslater.h" +#include +#include "query.h" +#include "querynodes.h" +#include "taoserror.h" #include "tdatablock.h" #include "catalog.h" @@ -1099,6 +1104,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)); @@ -1125,6 +1138,10 @@ static bool isVectorFunc(const SNode* pNode) { return (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsVectorFunc(((SFunctionNode*)pNode)->funcId)); } +static bool isColsFunc(const SNode* pNode) { + return (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsSelectColsFunc(((SFunctionNode*)pNode)->funcId)); +} + static bool isDistinctOrderBy(STranslateContext* pCxt) { return (SQL_CLAUSE_ORDER_BY == pCxt->currClause && isSelectStmt(pCxt->pCurrStmt) && ((SSelectStmt*)pCxt->pCurrStmt)->isDistinct); @@ -1557,24 +1574,27 @@ static int32_t findAndSetColumn(STranslateContext* pCxt, SColumnNode** pColRef, STempTableNode* pTempTable = (STempTableNode*)pTable; SNodeList* pProjectList = getProjectList(pTempTable->pSubquery); SNode* pNode; + SExprNode* pFoundExpr = NULL; FOREACH(pNode, pProjectList) { SExprNode* pExpr = (SExprNode*)pNode; - if (0 == strcmp(pCol->colName, pExpr->aliasName)) { + if (0 == strcmp(pCol->colName, pExpr->aliasName) || 0 == strcmp(pCol->colName, pExpr->userAlias)) { if (*pFound) { return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_AMBIGUOUS_COLUMN, pCol->colName); } - code = setColumnInfoByExpr(pTempTable, pExpr, pColRef); - if (TSDB_CODE_SUCCESS != code) { - break; - } + pFoundExpr = pExpr; *pFound = true; } else if (isPrimaryKeyImpl(pNode) && isInternalPrimaryKey(pCol)) { - code = setColumnInfoByExpr(pTempTable, pExpr, pColRef); - if (TSDB_CODE_SUCCESS != code) break; + pFoundExpr = pExpr; pCol->isPrimTs = true; *pFound = true; } } + if (pFoundExpr) { + code = setColumnInfoByExpr(pTempTable, pFoundExpr, pColRef); + if (TSDB_CODE_SUCCESS != code) { + return code; + } + } } return code; } @@ -2331,7 +2351,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; } @@ -2479,9 +2499,10 @@ static int32_t rewriteCountTbname(STranslateContext* pCxt, SFunctionNode* pCount return code; } -static bool hasInvalidFuncNesting(SNodeList* pParameterList) { +static bool hasInvalidFuncNesting(SFunctionNode* pFunc) { + if(pFunc->funcType == FUNCTION_TYPE_COLS) return false; bool hasInvalidFunc = false; - nodesWalkExprs(pParameterList, haveVectorFunction, &hasInvalidFunc); + nodesWalkExprs(pFunc->pParameterList, haveVectorFunction, &hasInvalidFunc); return hasInvalidFunc; } @@ -2503,7 +2524,7 @@ static int32_t translateAggFunc(STranslateContext* pCxt, SFunctionNode* pFunc) { if (beforeHaving(pCxt->currClause)) { return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION); } - if (hasInvalidFuncNesting(pFunc->pParameterList)) { + if (hasInvalidFuncNesting(pFunc)) { return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_AGG_FUNC_NESTING); } // The auto-generated COUNT function in the DELETE statement is legal @@ -2547,7 +2568,7 @@ static int32_t translateIndefiniteRowsFunc(STranslateContext* pCxt, SFunctionNod return generateSyntaxErrMsgExt(&pCxt->msgBuf, TSDB_CODE_PAR_NOT_ALLOWED_FUNC, "%s function is not supported in window query or group query", pFunc->functionName); } - if (hasInvalidFuncNesting(pFunc->pParameterList)) { + if (hasInvalidFuncNesting(pFunc)) { return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_AGG_FUNC_NESTING); } return TSDB_CODE_SUCCESS; @@ -2562,7 +2583,7 @@ static int32_t translateMultiRowsFunc(STranslateContext* pCxt, SFunctionNode* pF ((SSelectStmt*)pCxt->pCurrStmt)->hasMultiRowsFunc) { return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NOT_ALLOWED_FUNC); } - if (hasInvalidFuncNesting(pFunc->pParameterList)) { + if (hasInvalidFuncNesting(pFunc)) { return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_AGG_FUNC_NESTING); } return TSDB_CODE_SUCCESS; @@ -2593,7 +2614,7 @@ static int32_t translateInterpFunc(STranslateContext* pCxt, SFunctionNode* pFunc return generateSyntaxErrMsgExt(&pCxt->msgBuf, TSDB_CODE_PAR_NOT_ALLOWED_FUNC, "%s function is not supported in window query or group query", pFunc->functionName); } - if (hasInvalidFuncNesting(pFunc->pParameterList)) { + if (hasInvalidFuncNesting(pFunc)) { return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_AGG_FUNC_NESTING); } return TSDB_CODE_SUCCESS; @@ -2659,7 +2680,7 @@ static int32_t translateForecastFunc(STranslateContext* pCxt, SFunctionNode* pFu return generateSyntaxErrMsgExt(&pCxt->msgBuf, TSDB_CODE_PAR_NOT_ALLOWED_FUNC, "%s function is not supported in window query or group query", pFunc->functionName); } - if (hasInvalidFuncNesting(pFunc->pParameterList)) { + if (hasInvalidFuncNesting(pFunc)) { return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_AGG_FUNC_NESTING); } return TSDB_CODE_SUCCESS; @@ -2889,6 +2910,9 @@ static int32_t calcSelectFuncNum(SFunctionNode* pFunc, int32_t currSelectFuncNum if (fmIsCumulativeFunc(pFunc->funcId)) { return currSelectFuncNum > 0 ? currSelectFuncNum : 1; } + if(fmIsSelectColsFunc(pFunc->funcId)) { + return currSelectFuncNum; + } return currSelectFuncNum + ((fmIsMultiResFunc(pFunc->funcId) && !fmIsLastRowFunc(pFunc->funcId)) ? getMultiResFuncNum(pFunc->pParameterList) : 1); @@ -3314,6 +3338,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); } @@ -3555,6 +3583,8 @@ static EDealRes rewriteColToSelectValFunc(STranslateContext* pCxt, SNode** pNode tstrncpy(pFunc->functionName, "_select_value", TSDB_FUNC_NAME_LEN); tstrncpy(pFunc->node.aliasName, ((SExprNode*)*pNode)->aliasName, TSDB_COL_NAME_LEN); tstrncpy(pFunc->node.userAlias, ((SExprNode*)*pNode)->userAlias, TSDB_COL_NAME_LEN); + pFunc->node.bindTupleFuncIdx = ((SExprNode*)*pNode)->bindTupleFuncIdx; + pFunc->node.tupleFuncIdx = ((SExprNode*)*pNode)->tupleFuncIdx; pCxt->errCode = nodesListMakeAppend(&pFunc->pParameterList, *pNode); if (TSDB_CODE_SUCCESS == pCxt->errCode) { pCxt->errCode = getFuncInfo(pCxt, pFunc); @@ -3864,7 +3894,8 @@ static EDealRes doCheckExprForGroupBy(SNode** pNode, void* pContext) { } if (isScanPseudoColumnFunc(*pNode) || QUERY_NODE_COLUMN == nodeType(*pNode)) { - if (pSelect->selectFuncNum > 1 || (isDistinctOrderBy(pCxt) && pCxt->currClause == SQL_CLAUSE_ORDER_BY)) { + if ((pSelect->selectFuncNum > 1 || (isDistinctOrderBy(pCxt) && pCxt->currClause == SQL_CLAUSE_ORDER_BY)) && + ((SExprNode*)*pNode)->bindTupleFuncIdx == 0) { return generateDealNodeErrMsg(pCxt, getGroupByErrorCode(pCxt), ((SExprNode*)(*pNode))->userAlias); } if (isWindowJoinStmt(pSelect) && @@ -3873,7 +3904,7 @@ static EDealRes doCheckExprForGroupBy(SNode** pNode, void* pContext) { return rewriteExprToGroupKeyFunc(pCxt, pNode); } - if (pSelect->hasOtherVectorFunc || !pSelect->hasSelectFunc) { + if ((pSelect->hasOtherVectorFunc || !pSelect->hasSelectFunc) && ((SExprNode*)*pNode)->bindTupleFuncIdx == 0) { return generateDealNodeErrMsg(pCxt, getGroupByErrorCode(pCxt), ((SExprNode*)(*pNode))->userAlias); } @@ -3920,6 +3951,7 @@ static int32_t rewriteColsToSelectValFunc(STranslateContext* pCxt, SSelectStmt* typedef struct CheckAggColCoexistCxt { STranslateContext* pTranslateCxt; bool existCol; + bool hasColFunc; SNodeList* pColList; } CheckAggColCoexistCxt; @@ -3928,6 +3960,10 @@ static EDealRes doCheckAggColCoexist(SNode** pNode, void* pContext) { if (isVectorFunc(*pNode)) { return DEAL_RES_IGNORE_CHILD; } + if(isColsFunctionResult(*pNode)) { + pCxt->hasColFunc = true; + } + SNode* pPartKey = NULL; bool partionByTbname = false; if (fromSingleTable(((SSelectStmt*)pCxt->pTranslateCxt->pCurrStmt)->pFromTable) || @@ -3947,7 +3983,8 @@ static EDealRes doCheckAggColCoexist(SNode** pNode, void* pContext) { ((QUERY_NODE_COLUMN == nodeType(*pNode) && ((SColumnNode*)*pNode)->colType == COLUMN_TYPE_TAG))) { return rewriteExprToSelectTagFunc(pCxt->pTranslateCxt, pNode); } - if (isScanPseudoColumnFunc(*pNode) || QUERY_NODE_COLUMN == nodeType(*pNode)) { + if ((isScanPseudoColumnFunc(*pNode) || QUERY_NODE_COLUMN == nodeType(*pNode)) && + ((!nodesIsExprNode(*pNode) || ((SExprNode*)*pNode)->bindTupleFuncIdx == 0))) { pCxt->existCol = true; } return DEAL_RES_CONTINUE; @@ -4006,7 +4043,7 @@ static int32_t checkAggColCoexist(STranslateContext* pCxt, SSelectStmt* pSelect) if (!pSelect->onlyHasKeepOrderFunc) { pSelect->timeLineResMode = TIME_LINE_NONE; } - CheckAggColCoexistCxt cxt = {.pTranslateCxt = pCxt, .existCol = false}; + CheckAggColCoexistCxt cxt = {.pTranslateCxt = pCxt, .existCol = false, .hasColFunc = false}; nodesRewriteExprs(pSelect->pProjectionList, doCheckAggColCoexist, &cxt); if (!pSelect->isDistinct) { nodesRewriteExprs(pSelect->pOrderByList, doCheckAggColCoexist, &cxt); @@ -4018,6 +4055,9 @@ static int32_t checkAggColCoexist(STranslateContext* pCxt, SSelectStmt* pSelect) if (cxt.existCol) { return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NOT_SINGLE_GROUP); } + if (cxt.hasColFunc) { + return rewriteColsToSelectValFunc(pCxt, pSelect); + } return TSDB_CODE_SUCCESS; } @@ -5524,7 +5564,7 @@ static int32_t rewriteProjectAlias(SNodeList* pProjectionList) { if ('\0' == pExpr->userAlias[0]) { tstrncpy(pExpr->userAlias, pExpr->aliasName, TSDB_COL_NAME_LEN); } - snprintf(pExpr->aliasName, TSDB_COL_NAME_LEN,"#expr_%d", no++); + rewriteExprAliasName(pExpr, no++); } return TSDB_CODE_SUCCESS; } @@ -7300,11 +7340,329 @@ static int32_t translateSelectWithoutFrom(STranslateContext* pCxt, SSelectStmt* pCxt->dual = true; return translateExprList(pCxt, pSelect->pProjectionList); } +typedef struct SCheckColsFuncCxt { + bool hasColsFunc; + SNodeList** selectFuncList; + int32_t status; +} SCheckColsFuncCxt; + +static bool isColsFuncByName(SFunctionNode* pFunc) { + if (strcasecmp(pFunc->functionName, "cols") != 0) { + return false; + } + return true; +} + +static bool isMultiColsFuncNode(SNode* pNode) { + if (QUERY_NODE_FUNCTION == nodeType(pNode)) { + SFunctionNode* pFunc = (SFunctionNode*)pNode; + if (isColsFuncByName(pFunc)) { + if (pFunc->pParameterList->length > 2) { + return true; + } + } + } + return false; +} + +typedef struct SBindTupleFuncCxt { + int32_t bindTupleFuncIdx; +} SBindTupleFuncCxt; + +static EDealRes pushDownBindSelectFunc(SNode** pNode, void* pContext) { + SBindTupleFuncCxt* pCxt = pContext; + if (nodesIsExprNode(*pNode)) { + ((SExprNode*)*pNode)->bindTupleFuncIdx = pCxt->bindTupleFuncIdx; + int32_t len = strlen(((SExprNode*)*pNode)->aliasName); + if (len + TSDB_COL_NAME_EXLEN >= TSDB_COL_NAME_LEN) { + parserError("%s The alias name is too long, the extra part will be truncated", __func__); + return DEAL_RES_ERROR; + } else { + tsnprintf(((SExprNode*)*pNode)->aliasName + len, TSDB_COL_NAME_EXLEN, ".%d", pCxt->bindTupleFuncIdx); + } + SFunctionNode* pFunc = (SFunctionNode*)*pNode; + } + return DEAL_RES_CONTINUE; +} + +static bool invalidColsAlias(SFunctionNode* pFunc) { + if (pFunc->node.asAlias) { + if (pFunc->pParameterList->length > 2) { + return true; + } else { + SNode* pNode; + FOREACH(pNode, pFunc->pParameterList) { + SExprNode* pExpr = (SExprNode*)pNode; + if (pExpr->asAlias) { + return true; + } + } + } + } + return false; +} + +static int32_t getSelectFuncIndex(SNodeList* FuncNodeList, SNode* pSelectFunc) { + SNode* pNode = NULL; + int32_t selectFuncIndex = 0; + FOREACH(pNode, FuncNodeList) { + ++selectFuncIndex; + if (nodesEqualNode(pNode, pSelectFunc)) { + return selectFuncIndex; + } + } + return 0; +} + +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; + } + } + 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("%s Invalid cols function, the first parameter must be a select function", __func__); + return DEAL_RES_ERROR; + } + if (pFunc->node.asAlias) { + if (((SExprNode*)pExpr)->asAlias) { + pCxt->status = TSDB_CODE_INVALID_COLS_ALIAS; + parserError("%s Invalid using alias for cols function", __func__); + return DEAL_RES_ERROR; + } else { + ((SExprNode*)pExpr)->asAlias = true; + tstrncpy(((SExprNode*)pExpr)->userAlias, pFunc->node.userAlias, TSDB_COL_NAME_LEN); + } + } + 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("%s Invalid cols function, the first parameter must be a select function", __func__); + 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; + if (needRewrite) { + code = nodesMakeList(&pNewNodeList); + if (NULL == pNewNodeList) { + return code; + } + if (*selectFuncList == NULL) { + code = nodesMakeList(selectFuncList); + if (NULL == *selectFuncList) { + nodesDestroyList(pNewNodeList); + return code; + } + } + + SNode* pNewNode = NULL; + int32_t nums = 0; + int32_t selectFuncCount = (*selectFuncList)->length; + SNode* pTmpNode = NULL; + FOREACH(pTmpNode, *nodeList) { + if (isMultiColsFuncNode(pTmpNode)) { + SFunctionNode* pFunc = (SFunctionNode*)pTmpNode; + if(pFunc->node.asAlias) { + code = TSDB_CODE_INVALID_COLS_ALIAS; + parserError("%s Invalid using alias for cols function", __func__); + goto _end; + } + + SNode* pSelectFunc = nodesListGetNode(pFunc->pParameterList, 0); + if (nodeType(pSelectFunc) != QUERY_NODE_FUNCTION) { + code = TSDB_CODE_PAR_INVALID_COLS_FUNCTION; + parserError("%s Invalid cols function, the first parameter must be a select function", __func__); + 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("%s Invalid cols function, the first parameter must be a select function", __func__); + goto _end; + } + 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; + } + nodesDestroyList(*nodeList); + *nodeList = pNewNodeList; + return TSDB_CODE_SUCCESS; + } + _end: + if (TSDB_CODE_SUCCESS != code) { + nodesDestroyList(pNewNodeList); + } + return code; +} + +static int32_t translateColsFunction(STranslateContext* pCxt, SSelectStmt* pSelect) { + if (pSelect->pFromTable && QUERY_NODE_TEMP_TABLE == nodeType(pSelect->pFromTable)) { + SNode* pSubquery = ((STempTableNode*)pSelect->pFromTable)->pSubquery; + if (QUERY_NODE_SELECT_STMT == nodeType(pSubquery)) { + SSelectStmt* pSubSelect = (SSelectStmt*)pSubquery; + int32_t code = translateColsFunction(pCxt, pSubSelect); + if (TSDB_CODE_SUCCESS != code) { + return code; + } + } + } + SNodeList* selectFuncList = NULL; + int32_t code = rewriteColsFunction(pCxt, &pSelect->pProjectionList, &selectFuncList); + if (code == TSDB_CODE_SUCCESS) { + 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; +} static int32_t translateSelectFrom(STranslateContext* pCxt, SSelectStmt* pSelect) { pCxt->pCurrStmt = (SNode*)pSelect; pCxt->dual = false; - int32_t code = translateFrom(pCxt, &pSelect->pFromTable); + int32_t code = translateColsFunction(pCxt, pSelect); + if (TSDB_CODE_SUCCESS == code) { + code = translateFrom(pCxt, &pSelect->pFromTable); + } if (TSDB_CODE_SUCCESS == code) { pSelect->precision = ((STableNode*)pSelect->pFromTable)->precision; code = translateWhere(pCxt, pSelect); @@ -13572,6 +13930,10 @@ static int32_t extractQueryResultSchema(const SNodeList* pProjections, int32_t* int32_t index = 0; FOREACH(pNode, pProjections) { SExprNode* pExpr = (SExprNode*)pNode; + if(pExpr->tupleFuncIdx != 0) { + *numOfCols -= 1; + continue; + } if (TSDB_DATA_TYPE_NULL == pExpr->resType.type) { (*pSchema)[index].type = TSDB_DATA_TYPE_VARCHAR; (*pSchema)[index].bytes = VARSTR_HEADER_SIZE; diff --git a/source/libs/planner/src/planLogicCreater.c b/source/libs/planner/src/planLogicCreater.c index c3fd9cdcf2ae..b24ef5f12b7d 100644 --- a/source/libs/planner/src/planLogicCreater.c +++ b/source/libs/planner/src/planLogicCreater.c @@ -123,6 +123,8 @@ static EDealRes doRewriteExpr(SNode** pNode, void* pContext) { tstrncpy(pCol->node.userAlias, ((SExprNode*)pExpr)->userAlias, TSDB_COL_NAME_LEN); tstrncpy(pCol->colName, ((SExprNode*)pExpr)->aliasName, TSDB_COL_NAME_LEN); pCol->node.projIdx = ((SExprNode*)(*pNode))->projIdx; + pCol->node.bindTupleFuncIdx = ((SExprNode*)(*pNode))->bindTupleFuncIdx; + pCol->node.tupleFuncIdx = ((SExprNode*)(*pNode))->tupleFuncIdx; if (QUERY_NODE_FUNCTION == nodeType(pExpr)) { setColumnInfo((SFunctionNode*)pExpr, pCol, pCxt->isPartitionBy); } @@ -150,7 +152,7 @@ static EDealRes doNameExpr(SNode* pNode, void* pContext) { case QUERY_NODE_LOGIC_CONDITION: case QUERY_NODE_FUNCTION: { if ('\0' == ((SExprNode*)pNode)->aliasName[0]) { - snprintf(((SExprNode*)pNode)->aliasName, TSDB_COL_NAME_LEN, "#expr_%p", pNode); + rewriteExprAliasName((SExprNode*)pNode, (int64_t)pNode); } return DEAL_RES_IGNORE_CHILD; } @@ -710,6 +712,8 @@ static SColumnNode* createColumnByExpr(const char* pStmtName, SExprNode* pExpr) if (NULL != pStmtName) { snprintf(pCol->tableAlias, sizeof(pCol->tableAlias), "%s", pStmtName); } + pCol->node.bindTupleFuncIdx = pExpr->bindTupleFuncIdx; + pCol->node.tupleFuncIdx = pExpr->tupleFuncIdx; return pCol; } @@ -1596,6 +1600,9 @@ static int32_t createColumnByProjections(SLogicPlanContext* pCxt, const char* pS int32_t projIdx = 1; FOREACH(pNode, pExprs) { SColumnNode* pCol = createColumnByExpr(pStmtName, (SExprNode*)pNode); + if (pCol->node.tupleFuncIdx != 0) { + continue; + } if (TSDB_CODE_SUCCESS != (code = nodesListStrictAppend(pList, (SNode*)pCol))) { nodesDestroyList(pList); return code; diff --git a/source/libs/planner/src/planOptimizer.c b/source/libs/planner/src/planOptimizer.c index b9f5d4260433..715ab0467617 100644 --- a/source/libs/planner/src/planOptimizer.c +++ b/source/libs/planner/src/planOptimizer.c @@ -3035,7 +3035,7 @@ static int32_t smaIndexOptCreateSmaCols(SNodeList* pFuncs, uint64_t tableId, SNo } SExprNode exprNode; exprNode.resType = ((SExprNode*)pWsNode)->resType; - snprintf(exprNode.aliasName, TSDB_COL_NAME_LEN, "#expr_%d", index + 1); + rewriteExprAliasName(&exprNode, index + 1); SColumnNode* pkNode = NULL; code = smaIndexOptCreateSmaCol((SNode*)&exprNode, tableId, PRIMARYKEY_TIMESTAMP_COL_ID, &pkNode); if (TSDB_CODE_SUCCESS != code) { @@ -3478,6 +3478,10 @@ static bool eliminateProjOptCanChildConditionUseChildTargets(SLogicNode* pChild, nodesWalkExpr(pJoinLogicNode->pFullOnCond, eliminateProjOptCanUseNewChildTargetsImpl, &cxt); if (!cxt.canUse) return false; } + if (QUERY_NODE_LOGIC_PLAN_AGG == nodeType(pChild) && + ((SAggLogicNode*)pChild)->node.pTargets->length != pNewChildTargets->length) { + return false; + } return true; } @@ -3606,7 +3610,8 @@ static int32_t eliminateProjOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* } else { FOREACH(pProjection, pProjectNode->pProjections) { FOREACH(pChildTarget, pChild->pTargets) { - if (0 == strcmp(((SColumnNode*)pProjection)->colName, ((SColumnNode*)pChildTarget)->colName)) { + if (0 == strcmp(((SColumnNode*)pProjection)->colName, ((SColumnNode*)pChildTarget)->colName) + && ((SColumnNode*)pProjection)->node.tupleFuncIdx == 0) { SNode* pNew = NULL; code = nodesCloneNode(pChildTarget, &pNew); if (TSDB_CODE_SUCCESS == code) { diff --git a/source/libs/planner/src/planPhysiCreater.c b/source/libs/planner/src/planPhysiCreater.c index 31d51fad9bc1..5902f1c3e9ee 100644 --- a/source/libs/planner/src/planPhysiCreater.c +++ b/source/libs/planner/src/planPhysiCreater.c @@ -13,6 +13,7 @@ * along with this program. If not, see . */ +#include "nodes.h" #include "planInt.h" #include "catalog.h" @@ -30,98 +31,106 @@ typedef struct SSlotIndex { SArray* pSlotIdsInfo; // duplicate name slot } SSlotIndex; +static int64_t getExprBindIndexStr(SNode* pNode, char* bindInfo) { + if (nodeType(pNode) == QUERY_NODE_COLUMN) { + SColumnNode* pCol = (SColumnNode*)pNode; + if (pCol->dataBlockId == 0) { + return 0; + } + } + if (nodesIsExprNode(pNode)) { + SExprNode* pExpr = (SExprNode*)pNode; + if (pExpr->bindTupleFuncIdx > 0 && pExpr->bindTupleFuncIdx <= 9) { + bindInfo[0] = '0' + pExpr->bindTupleFuncIdx; + return 1; + } else if (pExpr->bindTupleFuncIdx != 0) { + return tsnprintf(bindInfo, sizeof(bindInfo), "%d", pExpr->bindTupleFuncIdx); + } + } + return 0; +} + +enum { + SLOT_KEY_TYPE_ALL = 1, + SLOT_KEY_TYPE_COLNAME = 2, +}; + +static int32_t getSlotKeyHelper(SNode* pNode, const char* pPreName, const char* name, char** ppKey, int32_t callocLen, + int32_t* pLen, uint16_t extraBufLen, int8_t slotKeyType) { + int32_t code = 0; + char bindInfo[16] = {0}; + //int exBindInfoLen = getExprBindIndexStr(pNode, bindInfo); + *ppKey = taosMemoryCalloc(1, callocLen); + if (!*ppKey) { + return terrno; + } + if (slotKeyType == SLOT_KEY_TYPE_ALL) { + TAOS_STRNCAT(*ppKey, pPreName, TSDB_TABLE_NAME_LEN); + TAOS_STRNCAT(*ppKey, ".", 2); + TAOS_STRNCAT(*ppKey, name, TSDB_COL_NAME_LEN); + // if (exBindInfoLen > 0) { + // TAOS_STRNCAT(*ppKey, bindInfo, exBindInfoLen); + // } + *pLen = taosHashBinary(*ppKey, strlen(*ppKey)); + } else { + TAOS_STRNCAT(*ppKey, name, TSDB_COL_NAME_LEN); + *pLen = strlen(*ppKey); + } + + return code; +} + static int32_t getSlotKey(SNode* pNode, const char* pStmtName, char** ppKey, int32_t* pLen, uint16_t extraBufLen) { int32_t code = 0; + int32_t callocLen = 0; if (QUERY_NODE_COLUMN == nodeType(pNode)) { SColumnNode* pCol = (SColumnNode*)pNode; if (NULL != pStmtName) { if ('\0' != pStmtName[0]) { - *ppKey = taosMemoryCalloc(1, TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen); - if (!*ppKey) { - return terrno; - } - TAOS_STRNCAT(*ppKey, pStmtName, TSDB_TABLE_NAME_LEN); - TAOS_STRNCAT(*ppKey, ".", 2); - TAOS_STRNCAT(*ppKey, pCol->node.aliasName, TSDB_COL_NAME_LEN); - *pLen = taosHashBinary(*ppKey, strlen(*ppKey)); - return code; + callocLen = TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen; + return getSlotKeyHelper(pNode, pStmtName, pCol->node.aliasName, ppKey, callocLen, pLen, extraBufLen, + SLOT_KEY_TYPE_ALL); } else { - *ppKey = taosMemoryCalloc(1, TSDB_COL_NAME_LEN + 1 + extraBufLen); - if (!*ppKey) { - return terrno; - } - TAOS_STRNCAT(*ppKey, pCol->node.aliasName, TSDB_COL_NAME_LEN); - *pLen = strlen(*ppKey); - return code; + callocLen = TSDB_COL_NAME_LEN + 1 + extraBufLen; + return getSlotKeyHelper(pNode, pStmtName, pCol->node.aliasName, ppKey, callocLen, pLen, extraBufLen, + SLOT_KEY_TYPE_COLNAME); } } if ('\0' == pCol->tableAlias[0]) { - *ppKey = taosMemoryCalloc(1, TSDB_COL_NAME_LEN + 1 + extraBufLen); - if (!*ppKey) { - return terrno; - } - TAOS_STRNCAT(*ppKey, pCol->colName, TSDB_COL_NAME_LEN); - *pLen = strlen(*ppKey); - return code; + callocLen = TSDB_COL_NAME_LEN + 1 + extraBufLen; + return getSlotKeyHelper(pNode, pStmtName, pCol->colName, ppKey, callocLen, pLen, extraBufLen, + SLOT_KEY_TYPE_COLNAME); } - - *ppKey = taosMemoryCalloc(1, TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen); - if (!*ppKey) { - return terrno; - } - TAOS_STRNCAT(*ppKey, pCol->tableAlias, TSDB_TABLE_NAME_LEN); - TAOS_STRNCAT(*ppKey, ".", 2); - TAOS_STRNCAT(*ppKey, pCol->colName, TSDB_COL_NAME_LEN); - *pLen = taosHashBinary(*ppKey, strlen(*ppKey)); - return code; + callocLen = TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen; + return getSlotKeyHelper(pNode, pCol->tableAlias, pCol->colName, ppKey, callocLen, pLen, extraBufLen, + SLOT_KEY_TYPE_ALL); } else if (QUERY_NODE_FUNCTION == nodeType(pNode)) { SFunctionNode* pFunc = (SFunctionNode*)pNode; if (FUNCTION_TYPE_TBNAME == pFunc->funcType) { SValueNode* pVal = (SValueNode*)nodesListGetNode(pFunc->pParameterList, 0); if (pVal) { if (NULL != pStmtName && '\0' != pStmtName[0]) { - *ppKey = taosMemoryCalloc(1, TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen); - if (!*ppKey) { - return terrno; - } - TAOS_STRNCAT(*ppKey, pStmtName, TSDB_TABLE_NAME_LEN); - TAOS_STRNCAT(*ppKey, ".", 2); - TAOS_STRNCAT(*ppKey, ((SExprNode*)pNode)->aliasName, TSDB_COL_NAME_LEN); - *pLen = taosHashBinary(*ppKey, strlen(*ppKey)); - return code; + callocLen = TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen; + return getSlotKeyHelper(pNode, pStmtName, ((SExprNode*)pNode)->aliasName, ppKey, callocLen, pLen, extraBufLen, + SLOT_KEY_TYPE_ALL); } int32_t literalLen = strlen(pVal->literal); - *ppKey = taosMemoryCalloc(1, literalLen + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen); - if (!*ppKey) { - return terrno; - } - TAOS_STRNCAT(*ppKey, pVal->literal, literalLen); - TAOS_STRNCAT(*ppKey, ".", 2); - TAOS_STRNCAT(*ppKey, ((SExprNode*)pNode)->aliasName, TSDB_COL_NAME_LEN); - *pLen = taosHashBinary(*ppKey, strlen(*ppKey)); - return code; + callocLen = literalLen + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen; + return getSlotKeyHelper(pNode, pVal->literal, ((SExprNode*)pNode)->aliasName, ppKey, callocLen, pLen, + extraBufLen, SLOT_KEY_TYPE_ALL); } } } if (NULL != pStmtName && '\0' != pStmtName[0]) { - *ppKey = taosMemoryCalloc(1, TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen); - if (!*ppKey) { - return terrno; - } - TAOS_STRNCAT(*ppKey, pStmtName, TSDB_TABLE_NAME_LEN); - TAOS_STRNCAT(*ppKey, ".", 2); - TAOS_STRNCAT(*ppKey, ((SExprNode*)pNode)->aliasName, TSDB_COL_NAME_LEN); - *pLen = taosHashBinary(*ppKey, strlen(*ppKey)); - return code; + callocLen = TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen; + return getSlotKeyHelper(pNode, pStmtName, ((SExprNode*)pNode)->aliasName, ppKey, callocLen, pLen, extraBufLen, + SLOT_KEY_TYPE_ALL); } - *ppKey = taosMemoryCalloc(1, TSDB_COL_NAME_LEN + 1 + extraBufLen); - if (!*ppKey) { - return terrno; - } - TAOS_STRNCAT(*ppKey, ((SExprNode*)pNode)->aliasName, TSDB_COL_NAME_LEN); - *pLen = strlen(*ppKey); + callocLen = TSDB_COL_NAME_LEN + 1 + extraBufLen; + return getSlotKeyHelper(pNode, pStmtName, ((SExprNode*)pNode)->aliasName, ppKey, callocLen, pLen, extraBufLen, + SLOT_KEY_TYPE_COLNAME); return code; } diff --git a/source/libs/planner/src/planUtil.c b/source/libs/planner/src/planUtil.c index f03e2d8ab053..49559719a749 100644 --- a/source/libs/planner/src/planUtil.c +++ b/source/libs/planner/src/planUtil.c @@ -79,6 +79,8 @@ static EDealRes doCreateColumn(SNode* pNode, void* pContext) { } } } + pCol->node.bindTupleFuncIdx = pExpr->bindTupleFuncIdx; + pCol->node.tupleFuncIdx = pExpr->tupleFuncIdx; return (TSDB_CODE_SUCCESS == nodesListStrictAppend(pCxt->pList, (SNode*)pCol) ? DEAL_RES_IGNORE_CHILD : DEAL_RES_ERROR); } diff --git a/source/libs/scalar/src/sclfunc.c b/source/libs/scalar/src/sclfunc.c index ce431c0b1852..f3b56da37281 100644 --- a/source/libs/scalar/src/sclfunc.c +++ b/source/libs/scalar/src/sclfunc.c @@ -4402,3 +4402,4 @@ int32_t uniqueScalarFunction(SScalarParam *pInput, int32_t inputNum, SScalarPara int32_t modeScalarFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutput) { return selectScalarFunction(pInput, inputNum, pOutput); } + diff --git a/source/util/src/terror.c b/source/util/src/terror.c index ba2d471ccf28..57150cdc9d4e 100644 --- a/source/util/src/terror.c +++ b/source/util/src/terror.c @@ -750,8 +750,13 @@ TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_ANOMALY_WIN_TYPE, "ANOMALY_WINDOW only TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_ANOMALY_WIN_COL, "ANOMALY_WINDOW not support on tag column") TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_ANOMALY_WIN_OPT, "ANOMALY_WINDOW option should include algo field") TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_FORECAST_CLAUSE, "Invalid forecast clause") -TAOS_DEFINE_ERROR(TSDB_CODE_PAR_REGULAR_EXPRESSION_ERROR, "Syntax error in regular expression") +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/parallel_test/cases.task b/tests/parallel_test/cases.task index 0201c88d2b26..b3771e1e7a82 100644 --- a/tests/parallel_test/cases.task +++ b/tests/parallel_test/cases.task @@ -1118,6 +1118,10 @@ ,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/odbc.py ,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/fill_with_group.py ,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/state_window.py -Q 3 +,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/cols_function.py +,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/cols_function.py -Q 2 +,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/cols_function.py -Q 3 +,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/cols_function.py -Q 4 ,,y,system-test,./pytest.sh python3 ./test.py -f 99-TDcase/TD-21561.py -Q 4 ,,y,system-test,./pytest.sh python3 ./test.py -f 99-TDcase/TD-20582.py ,,n,system-test,python3 ./test.py -f 5-taos-tools/taosbenchmark/insertMix.py -N 3 diff --git a/tests/pytest/util/sql.py b/tests/pytest/util/sql.py index cdfe3ce8a0ec..bfdbff3826d7 100644 --- a/tests/pytest/util/sql.py +++ b/tests/pytest/util/sql.py @@ -648,6 +648,15 @@ def checkColNameList(self, col_name_list, expect_col_name_list): caller = inspect.getframeinfo(inspect.stack()[1][0]) args = (caller.filename, caller.lineno, self.sql, col_name_list, expect_col_name_list) tdLog.exit("%s(%d) failed: sql:%s, col_name_list:%s != expect_col_name_list:%s" % args) + + def checkResColNameList(self, expect_col_name_list): + col_name_list = [] + col_type_list = [] + for query_col in self.cursor.description: + col_name_list.append(query_col[0]) + col_type_list.append(query_col[1]) + + self.checkColNameList(col_name_list, expect_col_name_list) def __check_equal(self, elm, expect_elm): if elm == expect_elm: diff --git a/tests/system-test/2-query/cols_function.py b/tests/system-test/2-query/cols_function.py new file mode 100644 index 000000000000..085c95dd9a26 --- /dev/null +++ b/tests/system-test/2-query/cols_function.py @@ -0,0 +1,957 @@ +import random +import string +from util.log import * +from util.cases import * +from util.sql import * +from util.common import * +import numpy as np + + +class TDTestCase: + def init(self, conn, logSql, replicaVar=1): + self.replicaVar = int(replicaVar) + tdLog.debug("start to execute %s" % __file__) + tdSql.init(conn.cursor()) + + self.dbname = 'test' + + def condition_check(self, condition, row, col, expected_value): + if condition: + tdSql.checkData(row, col, expected_value) + + def create_test_data(self): + tdSql.execute(f'create database if not exists {self.dbname};') + tdSql.execute(f'use {self.dbname}') + tdSql.execute(f'drop table if exists {self.dbname}.meters') + + # tdLog.info("create test data") + # tdLog.info("taosBenchmark -y -t 10 -n 100 -b INT,FLOAT,NCHAR,BOOL") + # os.system("taosBenchmark -y -t 10 -n 100 -b INT,FLOAT,NCHAR,BOOL") + + tdSql.execute(f'create table {self.dbname}.meters (ts timestamp, c0 int, c1 float, c2 nchar(30), c3 bool) tags (t1 nchar(30))') + tdSql.execute(f'create table {self.dbname}.d0 using {self.dbname}.meters tags("st1")') + tdSql.execute(f'create table {self.dbname}.d1 using {self.dbname}.meters tags("st2")') + tdSql.execute(f'insert into {self.dbname}.d0 values(1734574929000, 1, 1, "c2", true)') + tdSql.execute(f'insert into {self.dbname}.d0 values(1734574929001, 2, 2, "bbbbbbbbb1", false)') + tdSql.execute(f'insert into {self.dbname}.d0 values(1734574929002, 2, 2, "bbbbbbbbb1", false)') + tdSql.execute(f'insert into {self.dbname}.d0 values(1734574929003, 3, 3, "a2", true)') + tdSql.execute(f'insert into {self.dbname}.d0 values(1734574929004, 4, 4, "bbbbbbbbb2", false)') + + tdSql.execute(f'insert into {self.dbname}.d1 values(1734574929000, 1, 1, "c2", true)') + + tdSql.execute(f'use {self.dbname}') + tdSql.execute(f'Create table {self.dbname}.normal_table (ts timestamp, c0 int, c1 float, c2 nchar(30), c3 bool)') + tdSql.execute(f'insert into {self.dbname}.normal_table (select * from {self.dbname}.d0)') + + def one_cols_1output_test(self): + tdLog.info("one_cols_1output_test") + tdSql.query(f'select cols(last(ts), ts) from {self.dbname}.meters') + tdSql.checkResColNameList(['ts']) + tdSql.query(f'select cols(last(ts), ts) as t1 from {self.dbname}.meters') + tdSql.checkResColNameList(['t1']) + tdSql.query(f'select cols(last(ts), ts as t1) from {self.dbname}.meters') + tdSql.checkResColNameList(['t1']) + tdSql.query(f'select cols(last(ts), c0) from {self.dbname}.meters') + tdSql.checkResColNameList(['c0']) + tdSql.query(f'select cols(last(ts), c1) from {self.dbname}.meters group by tbname') + tdSql.checkResColNameList(['c1']) + + + tdSql.query(f'select cols(last(ts+1), ts) as t1 from {self.dbname}.meters') + tdSql.checkResColNameList(['t1']) + tdSql.query(f'select cols(last(ts+1), ts+2 as t1) from {self.dbname}.meters') + tdSql.checkResColNameList(['t1']) + tdSql.query(f'select cols(last(ts+1), c0+10) from {self.dbname}.meters') + tdSql.checkResColNameList(['c0+10']) + + def one_cols_multi_output_with_group_test(self, from_table = 'test.meters', isTmpTable = False): + select_t1 = ["", ", t1", ", t1 as tag1"] + for t1 in select_t1: + tags_count = 0 if t1 == "" else 1 + tdLog.info("one_cols_1output_test_with_group") + tdSql.query(f'select cols(last(c1), ts) {t1} from {from_table} group by tbname') + tdSql.checkRows(2) + tdSql.checkCols(1 + tags_count) + tdSql.query(f'select cols(last(c1), ts) {t1} from {from_table} group by tbname order by tbname') + tdSql.checkCols(1 + tags_count) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(1, 0, 1734574929000) + tdSql.query(f'select cols(last(c1), ts) {t1} from {from_table} group by tbname order by ts') + tdSql.checkCols(1 + tags_count) + tdSql.checkData(0, 0, 1734574929000) + tdSql.checkData(1, 0, 1734574929004) + tdSql.query(f'select cols(last(c1), ts), tbname {t1} from {from_table} group by tbname order by tbname') + tdSql.checkRows(2) + tdSql.checkCols(2 + tags_count) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 'd0') + tdSql.checkData(1, 0, 1734574929000) + tdSql.checkData(1, 1, 'd1') + tdSql.query(f'select cols(last(c1), ts), tbname, t1 from {from_table} group by tbname order by tbname') + tdSql.checkRows(2) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 'd0') + tdSql.checkData(0, 2, 'st1') + tdSql.checkData(1, 0, 1734574929000) + tdSql.checkData(1, 1, 'd1') + tdSql.checkData(1, 2, 'st2') + tdSql.query(f'select cols(last(c1), ts), tbname, t1 from {from_table} group by tbname order by t1') + tdSql.checkResColNameList(['ts', 'tbname', 't1']) + tdSql.checkRows(2) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 'd0') + tdSql.checkData(0, 2, 'st1') + tdSql.checkData(1, 0, 1734574929000) + tdSql.checkData(1, 1, 'd1') + tdSql.checkData(1, 2, 'st2') + tdSql.query(f'select cols(last(ts), ts, c0), count(1) {t1} from {from_table} group by t1 order by t1') + tdSql.checkRows(2) + tdSql.checkCols(3 + tags_count) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 5) + self.condition_check(t1!="", 0, 3, 'st1') + tdSql.checkData(1, 0, 1734574929000) + tdSql.checkData(1, 1, 1) + tdSql.checkData(1, 2, 1) + self.condition_check(t1!="", 1, 3, 'st2') + + tdSql.query(f'select cols(last(ts), ts, c0), sum(c0) {t1} from {from_table} group by t1 order by t1') + tdSql.checkRows(2) + tdSql.checkCols(3 + tags_count) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 12) + self.condition_check(t1!="", 0, 3, 'st1') + tdSql.checkData(1, 0, 1734574929000) + tdSql.checkData(1, 1, 1) + tdSql.checkData(1, 2, 1) + self.condition_check(t1!="", 1, 3, 'st2') + + tdSql.error(f'select cols(last(ts), ts, c0), count(1), t1 from {from_table} group by t1 order by tbname') + + if t1 != "" and isTmpTable: + # Not a GROUP BY expression + tdSql.error(f'select cols(last(ts), ts, c0), count(1) {t1} from {from_table} group by tbname order by tbname') + tdSql.error(f'select cols(last(ts), ts, c0), count(1), tbname {t1} from {from_table} group by tbname order by tbname') + tdSql.error(f'select cols(max(c0), ts, c0), count(1), tbname {t1} from {from_table} group by tbname order by tbname') + tdSql.error(f'select cols(last(c1), ts), count(1) {t1} from {from_table} group by tbname') + continue + tdSql.query(f'select cols(last(ts), ts, c0), count(1) {t1} from {from_table} group by tbname order by tbname') + tdSql.checkRows(2) + tdSql.checkCols(3 + tags_count) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 5) + tdSql.checkData(1, 0, 1734574929000) + tdSql.checkData(1, 1, 1) + tdSql.checkData(1, 2, 1) + tdSql.query(f'select cols(last(ts), ts, c0), count(1), tbname {t1} from {from_table} group by tbname order by tbname') + tdSql.checkRows(2) + tdSql.checkCols(4 + tags_count) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 5) + tdSql.checkData(0, 3, 'd0') + tdSql.checkData(1, 0, 1734574929000) + tdSql.checkData(1, 1, 1) + tdSql.checkData(1, 2, 1) + tdSql.checkData(1, 3, 'd1') + tdSql.query(f'select cols(max(c0), ts, c0), count(1), tbname {t1} from {from_table} group by tbname order by tbname') + tdSql.checkRows(2) + tdSql.checkCols(4 + tags_count) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 5) + tdSql.checkData(0, 3, 'd0') + self.condition_check(t1!="", 0, 4, 'st1') + tdSql.checkData(1, 0, 1734574929000) + tdSql.checkData(1, 1, 1) + tdSql.checkData(1, 2, 1) + tdSql.checkData(1, 3, 'd1') + self.condition_check(t1!="", 1, 4, 'st2') + + tdSql.query(f'select cols(last(c1), ts), count(1) {t1} from {from_table} group by tbname') + tdSql.checkRows(2) + tdSql.checkCols(2 + tags_count) + + def one_cols_multi_output_test(self, from_table = 'test.meters'): + tdLog.info(f"one_cols_1output_test {from_table}") + tdSql.query(f'select cols(last(ts), ts, c0) from {from_table}') + tdSql.checkResColNameList(['ts', 'c0']) + tdSql.checkRows(1) + tdSql.checkCols(2) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.query(f'select cols(last(ts), ts as time, c0 cc) from {from_table}') + tdSql.checkResColNameList(['time', 'cc']) + tdSql.checkRows(1) + tdSql.checkCols(2) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.query(f'select cols(last(ts), ts as t123456789t123456789t123456789t123456789t123456789t123456789t123456789, c0 cc) from {from_table}') + tdSql.checkResColNameList(['t123456789t123456789t123456789t123456789t123456789t123456789t123', 'cc']) + tdSql.checkRows(1) + tdSql.checkCols(2) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.query(f'select cols(last(ts), c0, c1, c2, c3) from {from_table}') + tdSql.checkResColNameList(['c0', 'c1', 'c2', 'c3']) + tdSql.checkRows(1) + tdSql.checkCols(4) + tdSql.checkData(0, 0, 4) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 'bbbbbbbbb2') + tdSql.checkData(0, 3, False) + tdSql.query(f'select cols(last(ts), c0, t1) from {from_table}') + tdSql.checkResColNameList(['c0', 't1']) + tdSql.checkRows(1) + tdSql.checkData(0, 0, 4) + tdSql.checkData(0, 1, 'st1') + tdSql.query(f'select cols(max(c0), ts) from {from_table}') + tdSql.checkResColNameList(['ts']) + tdSql.checkCols(1) + tdSql.checkData(0, 0, 1734574929004) + tdSql.query(f'select cols(min(c1), ts, c0) from {from_table}') + tdSql.checkResColNameList(['ts', 'c0']) + tdSql.checkCols(2) + tdSql.checkData(0, 0, 1734574929000) + tdSql.checkData(0, 1, 1) + + tdSql.query(f'select cols(max(c0), ts, c0), count(1) from {from_table}') + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 6) + tdSql.query(f'select cols(last(ts), ts, c0), count(1) from {self.dbname}.d0') + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 5) + tdSql.query(f'select cols(last(ts), ts, c0), count(1) from {self.dbname}.d1') + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 1734574929000) + tdSql.checkData(0, 1, 1) + tdSql.checkData(0, 2, 1) + tdSql.query(f'select cols(last(ts), ts, c0), count(1) from {self.dbname}.normal_table') + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 5) + tdSql.query(f'select cols(first(ts), ts, c0), count(1) from {self.dbname}.normal_table') + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 1734574929000) + tdSql.checkData(0, 1, 1) + tdSql.checkData(0, 2, 5) + tdSql.query(f'select cols(min(c0), ts, c0), count(1) from {self.dbname}.normal_table') + tdSql.checkResColNameList(['ts', 'c0', 'count(1)']) + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 1734574929000) + tdSql.checkData(0, 1, 1) + tdSql.checkData(0, 2, 5) + + tdSql.query(f'select cols(last(ts), ts, c0), avg(c0) from {from_table}') + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 2.1666666666666665) + + tdSql.query(f'select cols(last(ts), ts, c0), count(1) from {from_table}') + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 6) + + tdSql.query(f'select cols(last(ts), ts, c0), sum(c0) from {from_table}') + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 13) + + tdSql.query(f'select count(1), cols(last(ts), ts, c0), min(c0) from {from_table}') + tdSql.checkRows(1) + tdSql.checkCols(4) + tdSql.checkData(0, 0, 6) + tdSql.checkData(0, 1, 1734574929004) + tdSql.checkData(0, 2, 4) + tdSql.checkData(0, 3, 1) + + # there's some error on last_row func when using sub query. + tdSql.query(f'select count(1), cols(last_row(ts), ts, c0), min(c0) from test.meters') + tdSql.checkRows(1) + tdSql.checkCols(4) + tdSql.checkData(0, 0, 6) + tdSql.checkData(0, 1, 1734574929004) + tdSql.checkData(0, 2, 4) + tdSql.checkData(0, 3, 1) + + # there's some error on last_row func when using sub query. + tdSql.query(f'select count(1), cols(last_row(ts), ts, c0), last_row(c1), last_row(c3) from test.meters') + tdSql.checkRows(1) + tdSql.checkCols(5) + tdSql.checkData(0, 0, 6) + tdSql.checkData(0, 1, 1734574929004) + tdSql.checkData(0, 2, 4) + tdSql.checkData(0, 3, 4) + tdSql.checkData(0, 4, False) + + + tdSql.query(f'select cols(last(ts), ts, c0), count(1) from {from_table}') + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 6) + + tdSql.query(f'select cols(max(c0), ts, c0), count(1) from {from_table}') + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 6) + + tdSql.query(f'select cols(last(ts), ts as time, c0 cc), count(1) from {from_table}') + tdSql.checkResColNameList(['time', 'cc', 'count(1)']) + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 6) + + tdSql.query(f'select cols(max(c1), ts as time, c0 cc), count(1) from {from_table}') + tdSql.checkResColNameList(['time', 'cc', 'count(1)']) + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 6) + + tdSql.query(f'select cols(last(ts), c0, c1, c2, c3), count(1) from {from_table}') + tdSql.checkResColNameList(['c0', 'c1', 'c2', 'c3', 'count(1)']) + tdSql.checkRows(1) + tdSql.checkCols(5) + tdSql.checkData(0, 0, 4) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 'bbbbbbbbb2') + tdSql.checkData(0, 3, False) + tdSql.checkData(0, 4, 6) + + tdSql.query(f'select cols(max(c0), ts), count(1) from {from_table}') + tdSql.query(f'select cols(min(c1), ts, c0), count(1) from {from_table}') + tdSql.query(f'select count(1), cols(max(c0), ts) from {from_table}') + tdSql.query(f'select max(c0), cols(max(c0), ts) from {from_table}') + tdSql.query(f'select max(c1), cols(max(c0), ts) from {from_table}') + + def multi_cols_output_test(self, from_table = 'test.meters', isTmpTable = False): + tdLog.info("multi_cols_output_test") + tdSql.query(f'select cols(last(c0), ts, c1), cols(first(c0), ts, c1), count(1) from {self.dbname}.meters') + tdSql.checkResColNameList(['ts', 'c1', 'ts', 'c1', 'count(1)']) + tdSql.checkRows(1) + tdSql.checkCols(5) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 1734574929000) + tdSql.checkData(0, 3, 1) + tdSql.checkData(0, 4, 6) + tdSql.query(f'select cols(last(c0),ts lts, c1 lc1), cols(first(c0), ts fts, c1 as fc1), count(1) from test.meters') + tdSql.checkResColNameList(['lts', 'lc1', 'fts', 'fc1', 'count(1)']) + tdSql.checkRows(1) + tdSql.checkCols(5) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 1734574929000) + tdSql.checkData(0, 3, 1) + tdSql.checkData(0, 4, 6) + tdSql.query(f'select cols(max(c0), ts as t1, c1 as c11), cols(first(c0), ts as t2, c1 c21), count(1) from {self.dbname}.meters') + tdSql.checkResColNameList(['t1', 'c11', 't2', 'c21', 'count(1)']) + tdSql.checkRows(1) + tdSql.checkCols(5) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 1734574929000) + tdSql.checkData(0, 3, 1) + tdSql.checkData(0, 4, 6) + tdSql.query(f'select cols(max(c0), ts as t1, c1 as c11), cols(first(c0), ts as t2, c1 c21), count(1) from {self.dbname}.meters where c0 < 4') + tdSql.checkResColNameList(['t1', 'c11', 't2', 'c21', 'count(1)']) + tdSql.checkRows(1) + tdSql.checkCols(5) + tdSql.checkData(0, 0, 1734574929003) + tdSql.checkData(0, 1, 3) + tdSql.checkData(0, 2, 1734574929000) + tdSql.checkData(0, 3, 1) + tdSql.checkData(0, 4, 5) + tdSql.query(f'select cols(max(c0), ts as t123456789t123456789t123456789t123456789t123456789t123456789t123456789, c1 as c11), cols(first(c0), \ + ts as t123456789t123456789t123456789t123456789t123456789t123456789t123456789, c1 c21), count(1) from {self.dbname}.meters') + tdSql.checkResColNameList(['t123456789t123456789t123456789t123456789t123456789t123456789t123', 'c11', \ + 't123456789t123456789t123456789t123456789t123456789t123456789t123', 'c21', 'count(1)']) + tdSql.checkRows(1) + tdSql.checkCols(5) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 1734574929000) + tdSql.checkData(0, 3, 1) + tdSql.checkData(0, 4, 6) + tdSql.query(f'select cols(max(c0), ts as t123456789t123456789t123456789t123456789t123456789t123456789t123456789, c1 as c11), cols(first(c0), \ + ts as t123456789t123456789t123456789t123456789t123456789t123456789t123456789, c1 c21), count(1) from {self.dbname}.meters where c0 < 4') + tdSql.checkRows(1) + tdSql.checkCols(5) + tdSql.checkData(0, 0, 1734574929003) + tdSql.checkData(0, 1, 3) + tdSql.checkData(0, 2, 1734574929000) + tdSql.checkData(0, 3, 1) + tdSql.checkData(0, 4, 5) + tdSql.query(f'select cols(max(c0), ts as t1, c1 as c11), cols(first(c0), ts as t2, c1 c21), count(1) from test.meters where c0 < 4 group by tbname order by t1') + tdSql.checkResColNameList(['t1', 'c11', 't2', 'c21', 'count(1)']) + tdSql.checkRows(2) + tdSql.checkCols(5) + tdSql.checkData(0, 0, 1734574929000) + tdSql.checkData(0, 1, 1) + tdSql.checkData(0, 2, 1734574929000) + tdSql.checkData(0, 3, 1) + tdSql.checkData(0, 4, 1) + tdSql.checkData(1, 0, 1734574929003) + tdSql.checkData(1, 1, 3) + tdSql.checkData(1, 2, 1734574929000) + tdSql.checkData(1, 3, 1) + tdSql.checkData(1, 4, 4) + + tdSql.query(f'select cols(last_row(c0), ts as t1, c1 as c11), cols(first(c0), ts as t2, c1 c21), count(1) from test.meters where c0 < 4 group by tbname order by t1') + tdSql.checkResColNameList(['t1', 'c11', 't2', 'c21', 'count(1)']) + tdSql.checkRows(2) + tdSql.checkCols(5) + tdSql.checkData(0, 0, 1734574929000) + tdSql.checkData(0, 1, 1) + tdSql.checkData(0, 2, 1734574929000) + tdSql.checkData(0, 3, 1) + tdSql.checkData(0, 4, 1) + tdSql.checkData(1, 0, 1734574929003) + tdSql.checkData(1, 1, 3) + tdSql.checkData(1, 2, 1734574929000) + tdSql.checkData(1, 3, 1) + tdSql.checkData(1, 4, 4) + + tdSql.query(f'select cols(last_row(c0), ts as t1, c1 as c11), cols(min(c0), ts as t2, c1 c21), count(1) from test.meters where c0 < 4 group by tbname order by t1') + tdSql.checkResColNameList(['t1', 'c11', 't2', 'c21', 'count(1)']) + tdSql.checkRows(2) + tdSql.checkCols(5) + tdSql.checkData(0, 0, 1734574929000) + tdSql.checkData(0, 1, 1) + tdSql.checkData(0, 2, 1734574929000) + tdSql.checkData(0, 3, 1) + tdSql.checkData(0, 4, 1) + tdSql.checkData(1, 0, 1734574929003) + tdSql.checkData(1, 1, 3) + tdSql.checkData(1, 2, 1734574929000) + tdSql.checkData(1, 3, 1) + tdSql.checkData(1, 4, 4) + + tdSql.query(f'select cols(last_row(c0), ts as t1, c1 as c11), cols(mode(c0), ts as t2, c1 c21), count(1) from test.meters where c0 < 4 group by tbname order by t1') + tdSql.checkResColNameList(['t1', 'c11', 't2', 'c21', 'count(1)']) + tdSql.checkRows(2) + tdSql.checkCols(5) + tdSql.checkData(0, 0, 1734574929000) + tdSql.checkData(0, 1, 1) + tdSql.checkData(0, 2, 1734574929000) + tdSql.checkData(0, 3, 1) + tdSql.checkData(0, 4, 1) + tdSql.checkData(1, 0, 1734574929003) + tdSql.checkData(1, 1, 3) + #tdSql.checkData(1, 2, 1734574929000) # mode(c0) is return a random ts of same c0 + tdSql.checkData(1, 3, 2) + tdSql.checkData(1, 4, 4) + + # fixed: has same select function outof cols func + tdSql.query(f'select cols(last_row(c0), ts as t1, c1 as c11), cols(first(c0), ts as t2, c1 c21), first(c0) from test.meters where c0 < 4 group by tbname order by t1') + tdSql.checkResColNameList(['t1', 'c11', 't2', 'c21', 'first(c0)']) + tdSql.checkRows(2) + tdSql.checkCols(5) + tdSql.checkData(0, 0, 1734574929000) + tdSql.checkData(0, 1, 1) + tdSql.checkData(0, 2, 1734574929000) + tdSql.checkData(0, 3, 1) + tdSql.checkData(0, 4, 1) + tdSql.checkData(1, 0, 1734574929003) + tdSql.checkData(1, 1, 3) + tdSql.checkData(1, 2, 1734574929000) + tdSql.checkData(1, 3, 1) + tdSql.checkData(1, 4, 1) + + tdSql.query(f'select cols(last_row(c0), ts as t1, c1 as c11), cols(first(c0), ts as t2, c1 c21), first(c0), cols(last(c0), ts, c1) from test.meters where c0 < 4 group by tbname order by t1;') + tdSql.checkResColNameList(['t1', 'c11', 't2', 'c21', 'first(c0)', 'ts', 'c1']) + tdSql.checkRows(2) + tdSql.checkCols(7) + tdSql.checkData(0, 0, 1734574929000) + tdSql.checkData(0, 1, 1) + tdSql.checkData(0, 2, 1734574929000) + tdSql.checkData(0, 3, 1) + tdSql.checkData(0, 4, 1) + tdSql.checkData(0, 5, 1734574929000) + tdSql.checkData(0, 6, 1) + tdSql.checkData(1, 0, 1734574929003) + tdSql.checkData(1, 1, 3) + tdSql.checkData(1, 2, 1734574929000) + tdSql.checkData(1, 3, 1) + tdSql.checkData(1, 4, 1) + tdSql.checkData(1, 5, 1734574929003) + tdSql.checkData(1, 6, 3) + + # sub query has cols func + tdSql.query(f'select c11 from (select cols(last_row(c0), ts as t1, c1 as c11), cols(first(c0), ts as t2, c1 c21), first(c0) from test.meters where c0 < 4)') + tdSql.checkRows(1) + tdSql.checkCols(1) + tdSql.checkData(0, 0, 3) + tdSql.query(f'select c11, c21 from (select cols(last_row(c0), ts as t1, c1 as c11), cols(first(c0), ts as t2, c1 c21), first(c0) from test.meters where c0 < 4)') + tdSql.checkRows(1) + tdSql.checkCols(2) + tdSql.checkData(0, 0, 3) + tdSql.checkData(0, 1, 1) + tdSql.query(f'select c1, c21 from (select cols(last_row(c0), ts as t1, c1), cols(first(c0), ts as t2, c1 c21), first(c0) from test.meters where c0 < 4)') + tdSql.checkRows(1) + tdSql.checkCols(2) + tdSql.checkData(0, 0, 3) + tdSql.checkData(0, 1, 1) + tdSql.error(f'select c1 from (select cols(last_row(c0), ts as t1, c1), cols(first(c0), ts as t2, c1), first(c0) from test.meters where c0 < 4)') + + # cols on system table + tdSql.query(f'select cols(max(vgroup_id), uid) from information_schema.ins_tables') + tdSql.checkRows(1) + tdSql.checkCols(1) + tdSql.query(f'select cols(max(vgroup_id), uid, `ttl`, create_time) from information_schema.ins_tables') + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.query(f'select cols(max(vgroup_id), uid as uidname) from information_schema.ins_tables') + tdSql.checkRows(1) + tdSql.checkCols(1) + tdSql.error(f'select cols(last(vgroup_id), uid, `ttl`, create_time) from information_schema.ins_tables') + tdSql.error(f'select cols(first(vgroup_id), uid, `ttl`, create_time) from information_schema.ins_tables') + + def funcSupperTableTest(self): + tdSql.execute('create database if not exists db;') + tdSql.execute('use db') + tdSql.execute(f'drop table if exists db.st') + + tdSql.execute('create table db.st (ts timestamp, c0 int, c1 float, c2 nchar(30), c3 bool) tags (t1 nchar(30))') + tdSql.execute('create table db.st_1 using db.st tags("st1")') + tdSql.execute('create table db.st_2 using db.st tags("st1")') + tdSql.execute('insert into db.st_1 values(1734574929000, 1, 1, "c2", true)') + tdSql.execute('insert into db.st_1 values(1734574929001, 2, 2, "bbbbbbbbb1", false)') + tdSql.execute('insert into db.st_1 values(1734574929002, 3, 3, "a2", true)') + tdSql.execute('insert into db.st_1 values(1734574929004, 4, 4, "bbbbbbbbb2", false)') + + tdSql.query(f'select cols(last(c0), ts, c1, c2, c3), cols(first(c0), ts, c1, c2, c3) from db.st') + tdSql.checkRows(1) + tdSql.checkData(0, 0, 1734574929004) + tdSql.checkData(0, 1, '4.0') + tdSql.checkData(0, 2, 'bbbbbbbbb2') + tdSql.checkData(0, 3, False) + tdSql.checkData(0, 4, 1734574929000) + tdSql.checkData(0, 5, '1.0') + tdSql.checkData(0, 6, 'c2') + tdSql.checkData(0, 7, True) + + tdSql.execute(f'drop table if exists db.st') + + + def funcNestTest(self): + tdSql.execute('create database db;') + tdSql.execute('use db') + tdSql.execute(f'drop table if exists db.d1') + + tdSql.execute('create table db.d1 (ts timestamp, c0 int, c1 float, c2 nchar(30), c3 bool)') + tdSql.execute('insert into db.d1 values(1734574929000, 1, 1.1, "a", true)') + tdSql.execute('insert into db.d1 values(1734574930000, 2, 2.2, "bbbbbbbbb", false)') + + groupby = ["", "group by tbname order ts", "group by tbname order t1", "group by tbname order ts"] + + tdSql.query(f'select cols(last(c0), ts, c2), cols(first(c0), ts, c2) from db.d1') + tdSql.checkResColNameList(['ts', 'c2', 'ts', 'c2']) + tdSql.checkRows(1) + tdSql.checkCols(4) + tdSql.checkData(0, 0, 1734574930000) + tdSql.checkData(0, 1, 'bbbbbbbbb') + tdSql.checkData(0, 2, 1734574929000) + tdSql.checkData(0, 3, 'a') + tdSql.query(f'select cols(last(c0), ts, c1, c2, c3), cols(first(c0), ts, c1, c2, c3) from db.d1') + tdSql.checkResColNameList(['ts', 'c1', 'c2', 'c3', 'ts', 'c1', 'c2', 'c3']) + tdSql.checkRows(1) + tdSql.checkCols(8) + tdSql.checkData(0, 0, 1734574930000) + tdSql.checkData(0, 1, 2.2) + tdSql.checkData(0, 2, 'bbbbbbbbb') + tdSql.checkData(0, 3, False) + tdSql.checkData(0, 4, 1734574929000) + tdSql.checkData(0, 5, 1.1) + tdSql.checkData(0, 6, 'a') + tdSql.checkData(0, 7, True) + + tdSql.query(f'select cols(last(ts), c1), cols(first(ts), c1) from db.d1') + tdSql.checkResColNameList(['c1', 'c1']) + tdSql.checkRows(1) + tdSql.checkCols(2) + tdSql.checkData(0, 0, 2.2) + tdSql.checkData(0, 1, 1.1) + + tdSql.query(f'select cols(first(ts), c0, c1), cols(first(ts), c0, c1) from db.d1') + tdSql.checkResColNameList(['c0', 'c1', 'c0', 'c1']) + tdSql.checkRows(1) + tdSql.checkCols(4) + tdSql.checkData(0, 0, 1) + tdSql.checkData(0, 1, 1.1) + tdSql.checkData(0, 2, 1) + tdSql.checkData(0, 3, 1.1) + + tdSql.query(f'select cols(first(ts), c0, c1), cols(first(ts+1), c0, c1) from db.d1') + tdSql.checkResColNameList(['c0', 'c1', 'c0', 'c1']) + tdSql.checkRows(1) + tdSql.checkCols(4) + tdSql.checkData(0, 0, 1) + tdSql.checkData(0, 1, 1.1) + tdSql.checkData(0, 2, 1) + tdSql.checkData(0, 3, 1.1) + + tdSql.query(f'select cols(first(ts), c0, c1), cols(first(ts), c0+1, c1+2) from db.d1') + tdSql.checkResColNameList(['c0', 'c1', 'c0+1', 'c1+2']) + tdSql.checkRows(1) + tdSql.checkCols(4) + tdSql.checkData(0, 0, 1) + tdSql.checkData(0, 1, 1.1) + tdSql.checkData(0, 2, 2) + tdSql.checkData(0, 3, 3.1) + + tdSql.query(f'select cols(first(c0), ts, length(c2)), cols(last(c0), ts, length(c2)) from db.d1') + tdSql.checkResColNameList(['ts', 'length(c2)', 'ts', 'length(c2)']) + tdSql.checkRows(1) + tdSql.checkCols(4) + tdSql.checkData(0, 0, 1734574929000) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 1734574930000) + tdSql.checkData(0, 3, 36) + tdSql.query(f'select cols(first(c0), ts, length(c2)), cols(last(c0), ts, length(c2) + 2) from db.d1') + tdSql.checkResColNameList(['ts', 'length(c2)', 'ts', 'length(c2) + 2']) + tdSql.checkRows(1) + tdSql.checkCols(4) + tdSql.checkData(0, 0, 1734574929000) + tdSql.checkData(0, 1, 4) + tdSql.checkData(0, 2, 1734574930000) + tdSql.checkData(0, 3, 38) + + tdSql.query(f'select cols(first(c0), ts, c2), cols(last(c0), ts, length(c2) + 2) from db.d1') + tdSql.checkResColNameList(['ts', 'c2', 'ts', 'length(c2) + 2']) + tdSql.checkRows(1) + tdSql.checkCols(4) + tdSql.checkData(0, 0, 1734574929000) + tdSql.checkData(0, 1, 'a') + tdSql.checkData(0, 2, 1734574930000) + tdSql.checkData(0, 3, 38) + + tdSql.query(f'select cols(min(c0), ts, c2), cols(last(c0), ts, length(c2) + 2) from db.d1') + tdSql.checkResColNameList(['ts', 'c2', 'ts', 'length(c2) + 2']) + tdSql.checkRows(1) + tdSql.checkCols(4) + tdSql.checkData(0, 0, 1734574929000) + tdSql.checkData(0, 1, 'a') + tdSql.checkData(0, 2, 1734574930000) + tdSql.checkData(0, 3, 38) + + tdSql.query(f'select cols(min(c0), ts, c2), cols(first(c0), ts, length(c2) + 2) from db.d1') + tdSql.checkResColNameList(['ts', 'c2', 'ts', 'length(c2) + 2']) + tdSql.checkRows(1) + tdSql.checkCols(4) + tdSql.checkData(0, 0, 1734574929000) + tdSql.checkData(0, 1, 'a') + tdSql.checkData(0, 2, 1734574929000) + tdSql.checkData(0, 3, 6) + + def orderby_test(self, from_table = 'test.meters', isTmpTable = False): + select_t1 = ["", ", t1", ", t1 as tag1"] + for t1 in select_t1: + if t1 != "" and isTmpTable: + # Not a GROUP BY expression + tdSql.error(f'select count(1), cols(last(c0),c2) {t1} from {from_table} group by tbname order by cols(last(c0), c2)') + tdSql.error(f'select count(1), cols(last(c0),c2) {t1} from {from_table} group by tbname order by cols(last(c0), c2) desc') + tdSql.error(f'select count(1), cols(last(c0),c2) {t1} from {from_table} group by tbname order by cols(last(c0), c0)') + tdSql.error(f'select count(1), cols(last(c0),c2) {t1} from {from_table} group by tbname order by cols(last(c0), c0), cols(last(c0), c1)') + tdSql.error(f'select count(1), last(c2) {t1} from {from_table} group by tbname order by cols(last(c0), c2)') + tdSql.error(f'select count(1), last(c2) {t1} from {from_table} group by tbname order by cols(last(c0), c2) desc') + tdSql.error(f'select count(1), last(c2) {t1} from {from_table} group by tbname order by cols(last(c0), c0)') + tdSql.error(f'select count(1), last(c2) {t1} from {from_table} group by tbname order by cols(last(c0), c0), cols(last(c0), c1)') + tdSql.error(f'select count(1), max(c2) {t1} from {from_table} group by tbname order by cols(last(c0), c2)') + tdSql.error(f'select count(1), max(c2) {t1} from {from_table} group by tbname order by cols(last(c0), c2) desc') + tdSql.error(f'select count(1), max(c2) {t1} from {from_table} group by tbname order by cols(last(c0), c0)') + tdSql.error(f'select count(1), max(c2) {t1} from {from_table} group by tbname order by cols(last(c0), c0), cols(last(c0), c1)') + continue + tdSql.query(f'select count(1), cols(last(c0),c2) {t1} from {from_table} group by tbname order by cols(last(c0), c2)') + tdSql.checkRows(2) + tags_count = 0 if t1 == "" else 1 + tdLog.debug(f'tags_count: {tags_count}') + tdSql.checkCols(2 + tags_count) + tdSql.checkData(0, 0, 5) + tdSql.checkData(0, 1, 'bbbbbbbbb2') + self.condition_check(t1 != "", 0, 2, 'st1') + tdSql.checkData(1, 0, 1) + tdSql.checkData(1, 1, 'c2') + self.condition_check(t1 != "", 1, 2, 'st2') + + tdSql.query(f'select count(1), cols(last(c0),c2) {t1} from {from_table} group by tbname order by cols(last(c0), c2) desc') + tdSql.checkRows(2) + tdSql.checkCols(2 + tags_count) + tdSql.checkData(0, 0, 1) + tdSql.checkData(0, 1, 'c2') + self.condition_check(t1 != "", 0, 2, 'st2') + tdSql.checkData(1, 0, 5) + tdSql.checkData(1, 1, 'bbbbbbbbb2') + self.condition_check(t1 != "", 1, 2, 'st1') + + tdSql.query(f'select count(1), cols(last(c0),c2) {t1} from {from_table} group by tbname order by cols(last(c0), c0)') + tdSql.checkRows(2) + tdSql.checkCols(2 + tags_count) + tdSql.checkData(0, 0, 1) + tdSql.checkData(0, 1, 'c2') + self.condition_check(t1 != "", 0, 2, 'st2') + tdSql.checkData(1, 0, 5) + tdSql.checkData(1, 1, 'bbbbbbbbb2') + self.condition_check(t1 != "", 1, 2, 'st1') + + tdSql.query(f'select count(1), cols(last(c0),c2) {t1} from {from_table} group by tbname order by 1') + tdSql.checkRows(2) + tdSql.checkCols(2 + tags_count) + tdSql.checkData(0, 0, 1) + tdSql.checkData(0, 1, 'c2') + self.condition_check(t1 != "", 0, 2, 'st2') + tdSql.checkData(1, 0, 5) + tdSql.checkData(1, 1, 'bbbbbbbbb2') + self.condition_check(t1 != "", 1, 2, 'st1') + + tdSql.query(f'select count(1), cols(last(c0),c2) {t1} from {from_table} group by tbname order by 2') + tdSql.checkRows(2) + tdSql.checkCols(2 + tags_count) + tdSql.checkData(0, 0, 5) + tdSql.checkData(0, 1, 'bbbbbbbbb2') + self.condition_check(t1 != "", 0, 2, 'st1') + tdSql.checkData(1, 0, 1) + tdSql.checkData(1, 1, 'c2') + self.condition_check(t1 != "", 1, 2, 'st2') + + tdSql.query(f'select count(1), cols(last(c0),c2) {t1} from {from_table} group by tbname order by cols(last(c0), c0), cols(last(c0), c1)') + tdSql.checkRows(2) + tdSql.checkCols(2 + tags_count) + tdSql.checkData(0, 0, 1) + tdSql.checkData(0, 1, 'c2') + self.condition_check(t1 != "", 0, 2, 'st2') + tdSql.checkData(1, 0, 5) + tdSql.checkData(1, 1, 'bbbbbbbbb2') + self.condition_check(t1 != "", 1, 2, 'st1') + + tdSql.query(f'select count(1), last(c2) {t1} from {from_table} group by tbname order by cols(last(c0), c2)') + tdSql.checkRows(2) + tdSql.checkCols(2 + tags_count) + tdSql.checkData(0, 0, 5) + tdSql.checkData(0, 1, 'bbbbbbbbb2') + self.condition_check(t1 != "", 0, 2, 'st1') + tdSql.checkData(1, 0, 1) + tdSql.checkData(1, 1, 'c2') + self.condition_check(t1 != "", 1, 2, 'st2') + + tdSql.query(f'select count(1), last(c2) {t1} from {from_table} group by tbname order by cols(last(c0), c2) desc') + tdSql.checkRows(2) + tdSql.checkCols(2 + tags_count) + tdSql.checkData(0, 0, 1) + tdSql.checkData(0, 1, 'c2') + self.condition_check(t1 != "", 0, 2, 'st2') + tdSql.checkData(1, 0, 5) + tdSql.checkData(1, 1, 'bbbbbbbbb2') + self.condition_check(t1 != "", 1, 2, 'st1') + + tdSql.query(f'select count(1), last(c2) {t1} from {from_table} group by tbname order by cols(last(c0), c0)') + tdSql.checkRows(2) + tdSql.checkCols(2 + tags_count) + tdSql.checkData(0, 0, 1) + tdSql.checkData(0, 1, 'c2') + self.condition_check(t1 != "", 0, 2, 'st2') + tdSql.checkData(1, 0, 5) + tdSql.checkData(1, 1, 'bbbbbbbbb2') + self.condition_check(t1 != "", 1, 2, 'st1') + + tdSql.query(f'select count(1), last(c2) {t1} from {from_table} group by tbname order by cols(last(c0), c0), cols(last(c0), c1)') + tdSql.checkRows(2) + tdSql.checkCols(2 + tags_count) + tdSql.checkData(0, 0, 1) + tdSql.checkData(0, 1, 'c2') + self.condition_check(t1 != "", 0, 2, 'st2') + tdSql.checkData(1, 0, 5) + tdSql.checkData(1, 1, 'bbbbbbbbb2') + self.condition_check(t1 != "", 1, 2, 'st1') + + tdSql.query(f'select count(1), max(c2) {t1} from {from_table} group by tbname order by cols(last(c0), c2)') + tdSql.checkRows(2) + tdSql.checkCols(2 + tags_count) + tdSql.checkData(0, 0, 5) + tdSql.checkData(0, 1, 'c2') + self.condition_check(t1 != "", 0, 2, 'st1') + tdSql.checkData(1, 0, 1) + tdSql.checkData(1, 1, 'c2') + self.condition_check(t1 != "", 1, 2, 'st2') + + tdSql.query(f'select count(1), max(c2) {t1} from {from_table} group by tbname order by cols(last(c0), c2) desc') + tdSql.checkRows(2) + tdSql.checkCols(2 + tags_count) + tdSql.checkData(0, 0, 1) + tdSql.checkData(0, 1, 'c2') + self.condition_check(t1 != "", 0, 2, 'st2') + tdSql.checkData(1, 0, 5) + tdSql.checkData(1, 1, 'c2') + self.condition_check(t1 != "", 1, 2, 'st1') + + tdSql.query(f'select count(1), max(c2) {t1} from {from_table} group by tbname order by cols(last(c0), c0)') + tdSql.checkRows(2) + tdSql.checkCols(2 + tags_count) + tdSql.checkData(0, 0, 1) + tdSql.checkData(0, 1, 'c2') + self.condition_check(t1 != "", 0, 2, 'st2') + tdSql.checkData(1, 0, 5) + tdSql.checkData(1, 1, 'c2') + self.condition_check(t1 != "", 1, 2, 'st1') + + tdSql.query(f'select count(1), max(c2) {t1} from {from_table} group by tbname order by cols(last(c0), c0), cols(last(c0), c1)') + tdSql.checkRows(2) + tdSql.checkCols(2 + tags_count) + tdSql.checkData(0, 0, 1) + tdSql.checkData(0, 1, 'c2') + self.condition_check(t1 != "", 0, 2, 'st2') + tdSql.checkData(1, 0, 5) + tdSql.checkData(1, 1, 'c2') + self.condition_check(t1 != "", 1, 2, 'st1') + + def parse_test(self): + tdLog.info("parse test") + + #** error sql **# + tdSql.error(f'select cols(ts) from {self.dbname}.meters group by tbname') + tdSql.error(f'select cols(ts) from {self.dbname}.meters') + tdSql.error(f'select last(cols(ts)) from {self.dbname}.meters') + tdSql.error(f'select last(cols(ts, ts)) from {self.dbname}.meters') + tdSql.error(f'select last(cols(ts, ts), ts) from {self.dbname}.meters') + tdSql.error(f'select cols(last(ts), ts as t1) as t1 from {self.dbname}.meters') + tdSql.error(f'select cols(last(ts), ts, c0) t1 from {self.dbname}.meters') + tdSql.error(f'select cols(last(ts), ts t1) tt from {self.dbname}.meters') + tdSql.error(f'select cols(last(ts), c0 cc0, c1 cc1) cc from {self.dbname}.meters') + tdSql.error(f'select cols(last(ts), c0 as cc0) as cc from {self.dbname}.meters') + tdSql.error(f'select cols(ts) + 1 from {self.dbname}.meters group by tbname') + tdSql.error(f'select last(cols(ts)+1) from {self.dbname}.meters') + tdSql.error(f'select last(cols(ts+1, ts)) from {self.dbname}.meters') + tdSql.error(f'select last(cols(ts, ts), ts+1) from {self.dbname}.meters') + tdSql.error(f'select last(cols(last(ts+1), ts+1), ts) from {self.dbname}.meters') + tdSql.error(f'select cols(last(ts), ts+1 as t1) as t1 from {self.dbname}.meters') + tdSql.error(f'select cols(last(ts+1), ts, c0) t1 from {self.dbname}.meters') + 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') + + tdSql.error(f'select cols(cols(last(ts), c0), c0) as cc from {self.dbname}.meters') + tdSql.error(f'select cols(last(ts), cols(last(ts), c0), c0) as cc from {self.dbname}.meters') + + # Aggregate functions do not support nesting + tdSql.error(f'select count(1), cols(last_row(ts), ts, first(c0)), last_row(c1) from {self.dbname}.meters') + + # Not a GROUP BY expression + tdSql.error(f'select count(1), cols(last(c0),c0) from test.meters group by tbname order by c3 desc') + + def subquery_test(self): + tdSql.query(f'select count(1), cols(last(c0),c0) from (select * from test.d0)') + tdSql.query(f'select count(1), cols(last(c0),c0) from (select *, tbname from test.meters) group by tbname') + + tdLog.info("subquery_test: orderby_test from meters") + self.orderby_test("test.meters", False) + tdLog.info("subquery_test: orderby_test from (select *, tbname from meters)") + self.orderby_test("(select *, tbname from test.meters)", True) + tdLog.info("subquery_test: one_cols_multi_output_with_group_test from meters") + self.one_cols_multi_output_with_group_test("test.meters", False) + tdLog.info("subquery_test: one_cols_multi_output_with_group_test from (select *, tbname from meters)") + self.one_cols_multi_output_with_group_test("(select *, tbname from test.meters)", True) + + self.one_cols_multi_output_test("test.meters") + self.one_cols_multi_output_test("(select *, tbname from test.meters)") + + + def window_test(self): + tdSql.query(f'select tbname, _wstart,_wend, max(c0), max(c1), cols( max(c0), c1) from test.meters partition \ + by tbname count_window(2) order by tbname') + tdSql.checkRows(4) + tdSql.checkCols(6) + tdSql.checkData(0, 0, 'd0') + tdSql.checkData(3, 0, 'd1') + tdSql.checkData(3, 5, 1) + tdSql.query(f'select _wstart,_wend, max(c0), max(c1), cols( max(c0), c1) from test.d1 count_window(2);') + tdSql.checkRows(1) + tdSql.checkCols(5) + tdSql.checkData(0, 4, 1) + tdSql.query(f'select _wstart,_wend, max(c0), max(c1), cols( max(c0), c1) from test.normal_table count_window(2);') + tdSql.checkRows(3) + tdSql.checkCols(5) + tdSql.checkData(0, 4, 2) + + + def join_test(self): + tdSql.query(f'select cols(last(a.ts), a.c0) from test.d0 a join test.d1 b on a.ts = b.ts;') + tdSql.checkRows(1) + tdSql.checkCols(1) + tdSql.checkData(0, 0, 1) + tdSql.query(f'select cols(last(a.ts), a.c0) from test.d0 a join test.d1 b on a.ts = b.ts and a.c0 = b.c0;') + tdSql.checkRows(1) + tdSql.checkCols(1) + tdSql.checkData(0, 0, 1) + tdSql.query(f'select cols(last(a.ts), a.c0) from test.d0 a join test.d1 b on a.ts = b.ts and a.c0 > b.c0;') + tdSql.checkRows(0) + tdSql.query(f'select tbname, ts, c0 from (select cols(last(a.ts), a.tbname, a.ts, a.c0) from test.d0 a join test.d1 b on a.ts = b.ts and a.c0=b.c0)') + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 'd0') + tdSql.checkData(0, 1, 1734574929000) + tdSql.checkData(0, 2, 1) + tdSql.query(f'select tbname, ts, c0 from (select cols(first(a.ts), a.tbname, a.ts, a.c0) from test.d0 a join test.d1 b on a.ts = b.ts and a.c0=b.c0)') + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 'd0') + tdSql.checkData(0, 1, 1734574929000) + tdSql.checkData(0, 2, 1) + tdSql.error(f'select tbname, ts, c0 from (select cols(first(a.ts), a.tbname, a.ts, a.c0), cols(last(a.ts), b.tbname, b.ts, b.c0) from test.d0 a join test.d1 b on a.ts = b.ts and a.c0=b.c0)') + tdSql.query(f'select tbname, ts, c0 from (select cols(first(a.ts), a.tbname, a.ts, a.c0), cols(last(a.ts), b.tbname tbname1, b.ts ts2, b.c0 c02) from test.d0 a join test.d1 b on a.ts = b.ts and a.c0=b.c0)') + tdSql.checkRows(1) + tdSql.checkCols(3) + tdSql.checkData(0, 0, 'd0') + tdSql.checkData(0, 1, 1734574929000) + tdSql.checkData(0, 2, 1) + + def run(self): + self.funcNestTest() + self.funcSupperTableTest() + self.create_test_data() + self.parse_test() + self.one_cols_1output_test() + self.multi_cols_output_test() + self.subquery_test() + self.window_test() + self.join_test() + + def stop(self): + tdSql.close() + tdLog.success("%s successfully executed" % __file__) + + +tdCases.addWindows(__file__, TDTestCase()) +tdCases.addLinux(__file__, TDTestCase())