diff --git a/include/libs/function/functionMgt.h b/include/libs/function/functionMgt.h index f6304401fc0656ddab63302ca4aaace7d09180fc..1fa9db992729f26aa855d045c3d574a9e222d500 100644 --- a/include/libs/function/functionMgt.h +++ b/include/libs/function/functionMgt.h @@ -113,6 +113,9 @@ typedef enum EFunctionType { FUNCTION_TYPE_WENDTS, FUNCTION_TYPE_WDURATION, + // internal function + FUNCTION_TYPE_SELECT_VALUE, + // user defined funcion FUNCTION_TYPE_UDF = 10000 } EFunctionType; @@ -141,6 +144,7 @@ bool fmIsScalarFunc(int32_t funcId); bool fmIsNonstandardSQLFunc(int32_t funcId); bool fmIsStringFunc(int32_t funcId); bool fmIsDatetimeFunc(int32_t funcId); +bool fmIsSelectFunc(int32_t funcId); bool fmIsTimelineFunc(int32_t funcId); bool fmIsTimeorderFunc(int32_t funcId); bool fmIsPseudoColumnFunc(int32_t funcId); diff --git a/source/libs/function/inc/functionMgtInt.h b/source/libs/function/inc/functionMgtInt.h index e19a332e669d283b1cdd2ce11d02b09ddcbef12a..3869a5d7b2898bd1ea7e621513f6f13ea6d25d4b 100644 --- a/source/libs/function/inc/functionMgtInt.h +++ b/source/libs/function/inc/functionMgtInt.h @@ -39,6 +39,7 @@ extern "C" { #define FUNC_MGT_DYNAMIC_SCAN_OPTIMIZED FUNC_MGT_FUNC_CLASSIFICATION_MASK(10) #define FUNC_MGT_MULTI_RES_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(11) #define FUNC_MGT_SCAN_PC_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(12) +#define FUNC_MGT_SELECT_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(13) #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 8f3c88900dc194d1e432d041c403e624e52e0892..bd2fb9dca17cf3f59e1bfb6773ca8e4945337fe9 100644 --- a/source/libs/function/src/builtins.c +++ b/source/libs/function/src/builtins.c @@ -438,6 +438,11 @@ static int32_t translateToJson(SFunctionNode* pFunc, char* pErrBuf, int32_t len) return TSDB_CODE_SUCCESS; } +static int32_t translateSelectValue(SFunctionNode* pFunc, char* pErrBuf, int32_t len) { + pFunc->node.resType = ((SExprNode*)nodesListGetNode(pFunc->pParameterList, 0))->resType; + return TSDB_CODE_SUCCESS; +} + // clang-format off const SBuiltinFuncDefinition funcMgtBuiltins[] = { { @@ -465,7 +470,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = { { .name = "min", .type = FUNCTION_TYPE_MIN, - .classification = FUNC_MGT_AGG_FUNC | FUNC_MGT_SPECIAL_DATA_REQUIRED, + .classification = FUNC_MGT_AGG_FUNC | FUNC_MGT_SPECIAL_DATA_REQUIRED | FUNC_MGT_SELECT_FUNC, .translateFunc = translateInOutNum, .dataRequiredFunc = statisDataRequired, .getEnvFunc = getMinmaxFuncEnv, @@ -476,7 +481,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = { { .name = "max", .type = FUNCTION_TYPE_MAX, - .classification = FUNC_MGT_AGG_FUNC | FUNC_MGT_SPECIAL_DATA_REQUIRED, + .classification = FUNC_MGT_AGG_FUNC | FUNC_MGT_SPECIAL_DATA_REQUIRED | FUNC_MGT_SELECT_FUNC, .translateFunc = translateInOutNum, .dataRequiredFunc = statisDataRequired, .getEnvFunc = getMinmaxFuncEnv, @@ -974,6 +979,16 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = { .initFunc = NULL, .sprocessFunc = toJsonFunction, .finalizeFunc = NULL + }, + { + .name = "_select_value", + .type = FUNCTION_TYPE_SELECT_VALUE, + .classification = FUNC_MGT_AGG_FUNC | FUNC_MGT_SELECT_FUNC, + .translateFunc = translateSelectValue, + .getEnvFunc = NULL, + .initFunc = NULL, + .sprocessFunc = NULL, + .finalizeFunc = NULL } }; // clang-format on diff --git a/source/libs/function/src/functionMgt.c b/source/libs/function/src/functionMgt.c index b505f2e8ec94922447364ef2dae6c17762ffb58f..f8ef0f7d200e3a7479cda6afb4a06eaa8c0c6a43 100644 --- a/source/libs/function/src/functionMgt.c +++ b/source/libs/function/src/functionMgt.c @@ -145,6 +145,8 @@ bool fmIsAggFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MG bool fmIsScalarFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_SCALAR_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); } bool fmIsPseudoColumnFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_PSEUDO_COLUMN_FUNC); } diff --git a/source/libs/parser/src/parTranslater.c b/source/libs/parser/src/parTranslater.c index d9b250998e510378b9c959678ce9f7190692d6d3..4e2d75a79f0dd730939e8e15379eb1ebb506a4f9 100644 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -256,6 +256,26 @@ static void destroyTranslateContext(STranslateContext* pCxt) { taosHashCleanup(pCxt->pTables); } +static bool isAliasColumn(const SNode* pNode) { + return (QUERY_NODE_COLUMN == nodeType(pNode) && ('\0' == ((SColumnNode*)pNode)->tableAlias[0])); +} + +static bool isAggFunc(const SNode* pNode) { + return (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsAggFunc(((SFunctionNode*)pNode)->funcId)); +} + +static bool isSelectFunc(const SNode* pNode) { + return (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsSelectFunc(((SFunctionNode*)pNode)->funcId)); +} + +static bool isTimelineFunc(const SNode* pNode) { + return (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsTimelineFunc(((SFunctionNode*)pNode)->funcId)); +} + +static bool isDistinctOrderBy(STranslateContext* pCxt) { + return (SQL_CLAUSE_ORDER_BY == pCxt->currClause && pCxt->pCurrStmt->isDistinct); +} + static bool belongTable(const char* currentDb, const SColumnNode* pCol, const STableNode* pTable) { int cmp = 0; if ('\0' != pCol->dbName[0]) { @@ -617,13 +637,45 @@ static EDealRes translateOperator(STranslateContext* pCxt, SOperatorNode* pOp) { } static EDealRes haveAggFunction(SNode* pNode, void* pContext) { - if (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsAggFunc(((SFunctionNode*)pNode)->funcId)) { + if (isAggFunc(pNode)) { *((bool*)pContext) = true; return DEAL_RES_END; } return DEAL_RES_CONTINUE; } +static int32_t findTable(STranslateContext* pCxt, const char* pTableAlias, STableNode** pOutput) { + SArray* pTables = taosArrayGetP(pCxt->pNsLevel, pCxt->currLevel); + size_t nums = taosArrayGetSize(pTables); + for (size_t i = 0; i < nums; ++i) { + STableNode* pTable = taosArrayGetP(pTables, i); + if (NULL == pTableAlias || 0 == strcmp(pTable->tableAlias, pTableAlias)) { + *pOutput = pTable; + return TSDB_CODE_SUCCESS; + } + } + return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_TABLE_NOT_EXIST, pTableAlias); +} + +static bool isCountStar(SFunctionNode* pFunc) { + if (FUNCTION_TYPE_COUNT != pFunc->funcType || 1 != LIST_LENGTH(pFunc->pParameterList)) { + return false; + } + SNode* pPara = nodesListGetNode(pFunc->pParameterList, 0); + return (QUERY_NODE_COLUMN == nodeType(pPara) && 0 == strcmp(((SColumnNode*)pPara)->colName, "*")); +} + +// count(*) is rewritten as count(ts) for scannning optimization +static int32_t rewriteCountStar(STranslateContext* pCxt, SFunctionNode* pCount) { + SColumnNode* pCol = nodesListGetNode(pCount->pParameterList, 0); + STableNode* pTable = NULL; + int32_t code = findTable(pCxt, ('\0' == pCol->tableAlias[0] ? NULL : pCol->tableAlias), &pTable); + if (TSDB_CODE_SUCCESS == code && QUERY_NODE_REAL_TABLE == nodeType(pTable)) { + setColumnInfoBySchema((SRealTableNode*)pTable, ((SRealTableNode*)pTable)->pMeta->schema, false, pCol); + } + return code; +} + static EDealRes translateFunction(STranslateContext* pCxt, SFunctionNode* pFunc) { SFmGetFuncInfoParam param = {.pCtg = pCxt->pParseCxt->pCatalog, .pRpc = pCxt->pParseCxt->pTransporter, @@ -631,10 +683,7 @@ static EDealRes translateFunction(STranslateContext* pCxt, SFunctionNode* pFunc) .pErrBuf = pCxt->msgBuf.buf, .errBufLen = pCxt->msgBuf.len}; pCxt->errCode = fmGetFuncInfo(¶m, pFunc); - if (TSDB_CODE_SUCCESS != pCxt->errCode) { - return DEAL_RES_ERROR; - } - if (fmIsAggFunc(pFunc->funcId)) { + if (TSDB_CODE_SUCCESS == pCxt->errCode && fmIsAggFunc(pFunc->funcId)) { if (beforeHaving(pCxt->currClause)) { return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION); } @@ -643,11 +692,14 @@ static EDealRes translateFunction(STranslateContext* pCxt, SFunctionNode* pFunc) if (haveAggFunc) { return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_AGG_FUNC_NESTING); } + pCxt->pCurrStmt->hasAggFuncs = true; pCxt->pCurrStmt->isTimeOrderQuery = false; + if (isCountStar(pFunc)) { + pCxt->errCode = rewriteCountStar(pCxt, pFunc); + } } - - return DEAL_RES_CONTINUE; + return TSDB_CODE_SUCCESS == pCxt->errCode ? DEAL_RES_CONTINUE : DEAL_RES_ERROR; } static EDealRes translateExprSubquery(STranslateContext* pCxt, SNode* pNode) { @@ -691,12 +743,6 @@ static int32_t translateExprList(STranslateContext* pCxt, SNodeList* pList) { return pCxt->errCode; } -static bool isAliasColumn(SColumnNode* pCol) { return ('\0' == pCol->tableAlias[0]); } - -static bool isDistinctOrderBy(STranslateContext* pCxt) { - return (SQL_CLAUSE_ORDER_BY == pCxt->currClause && pCxt->pCurrStmt->isDistinct); -} - static SNodeList* getGroupByList(STranslateContext* pCxt) { if (isDistinctOrderBy(pCxt)) { return pCxt->pCurrStmt->pProjectionList; @@ -718,31 +764,71 @@ static int32_t getGroupByErrorCode(STranslateContext* pCxt) { return TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION; } -static EDealRes doCheckExprForGroupBy(SNode* pNode, void* pContext) { - STranslateContext* pCxt = (STranslateContext*)pContext; - if (!nodesIsExprNode(pNode) || (QUERY_NODE_COLUMN == nodeType(pNode) && isAliasColumn((SColumnNode*)pNode))) { +typedef struct SCheckExprForGroupByCxt { + STranslateContext* pTranslateCxt; + int32_t selectFuncNum; + bool hasSelectValFunc; +} SCheckExprForGroupByCxt; + +static EDealRes rewriteColToSelectValFunc(STranslateContext* pCxt, bool* pHasSelectValFunc, SNode** pNode) { + SFunctionNode* pFunc = nodesMakeNode(QUERY_NODE_FUNCTION); + if (NULL == pFunc) { + pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY; + return DEAL_RES_ERROR; + } + strcpy(pFunc->functionName, "_select_value"); + pCxt->errCode = nodesListMakeAppend(&pFunc->pParameterList, *pNode); + if (TSDB_CODE_SUCCESS == pCxt->errCode) { + translateFunction(pCxt, pFunc); + } + if (TSDB_CODE_SUCCESS == pCxt->errCode) { + *pNode = (SNode*)pFunc; + if (NULL != pHasSelectValFunc) { + *pHasSelectValFunc = true; + } + } else { + nodesDestroyNode(pFunc); + } + return TSDB_CODE_SUCCESS == pCxt->errCode ? DEAL_RES_IGNORE_CHILD : DEAL_RES_ERROR; +} + +static EDealRes doCheckExprForGroupBy(SNode** pNode, void* pContext) { + SCheckExprForGroupByCxt* pCxt = (SCheckExprForGroupByCxt*)pContext; + if (!nodesIsExprNode(*pNode) || isAliasColumn(*pNode)) { return DEAL_RES_CONTINUE; } - if (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsAggFunc(((SFunctionNode*)pNode)->funcId) && - !isDistinctOrderBy(pCxt)) { + pCxt->selectFuncNum += isSelectFunc(*pNode) ? 1 : 0; + if (pCxt->selectFuncNum > 1 && pCxt->hasSelectValFunc) { + return generateDealNodeErrMsg(pCxt->pTranslateCxt, getGroupByErrorCode(pCxt->pTranslateCxt)); + } + if (isAggFunc(*pNode) && !isDistinctOrderBy(pCxt->pTranslateCxt)) { return DEAL_RES_IGNORE_CHILD; } SNode* pGroupNode; - FOREACH(pGroupNode, getGroupByList(pCxt)) { - if (nodesEqualNode(getGroupByNode(pGroupNode), pNode)) { + FOREACH(pGroupNode, getGroupByList(pCxt->pTranslateCxt)) { + if (nodesEqualNode(getGroupByNode(pGroupNode), *pNode)) { return DEAL_RES_IGNORE_CHILD; } } - if (QUERY_NODE_COLUMN == nodeType(pNode) || - (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsAggFunc(((SFunctionNode*)pNode)->funcId) && - isDistinctOrderBy(pCxt))) { - return generateDealNodeErrMsg(pCxt, getGroupByErrorCode(pCxt)); + if (QUERY_NODE_COLUMN == nodeType(*pNode)) { + if (pCxt->selectFuncNum > 1) { + return generateDealNodeErrMsg(pCxt->pTranslateCxt, getGroupByErrorCode(pCxt->pTranslateCxt)); + } else { + return rewriteColToSelectValFunc(pCxt->pTranslateCxt, &pCxt->hasSelectValFunc, pNode); + } + } + if (isAggFunc(*pNode) && isDistinctOrderBy(pCxt->pTranslateCxt)) { + return generateDealNodeErrMsg(pCxt->pTranslateCxt, getGroupByErrorCode(pCxt->pTranslateCxt)); } return DEAL_RES_CONTINUE; } -static int32_t checkExprForGroupBy(STranslateContext* pCxt, SNode* pNode) { - nodesWalkExpr(pNode, doCheckExprForGroupBy, pCxt); +static int32_t checkExprForGroupBy(STranslateContext* pCxt, SNode** pNode) { + SCheckExprForGroupByCxt cxt = {.pTranslateCxt = pCxt, .selectFuncNum = 0, .hasSelectValFunc = false}; + nodesRewriteExpr(pNode, doCheckExprForGroupBy, &cxt); + if (cxt.selectFuncNum != 1 && cxt.hasSelectValFunc) { + return generateSyntaxErrMsg(&pCxt->msgBuf, getGroupByErrorCode(pCxt)); + } return pCxt->errCode; } @@ -750,7 +836,29 @@ static int32_t checkExprListForGroupBy(STranslateContext* pCxt, SNodeList* pList if (NULL == getGroupByList(pCxt)) { return TSDB_CODE_SUCCESS; } - nodesWalkExprs(pList, doCheckExprForGroupBy, pCxt); + SCheckExprForGroupByCxt cxt = {.pTranslateCxt = pCxt, .selectFuncNum = 0, .hasSelectValFunc = false}; + nodesRewriteExprs(pList, doCheckExprForGroupBy, &cxt); + if (cxt.selectFuncNum != 1 && cxt.hasSelectValFunc) { + return generateSyntaxErrMsg(&pCxt->msgBuf, getGroupByErrorCode(pCxt)); + } + return pCxt->errCode; +} + +static EDealRes rewriteColsToSelectValFuncImpl(SNode** pNode, void* pContext) { + if (isAggFunc(*pNode)) { + return DEAL_RES_IGNORE_CHILD; + } + if (QUERY_NODE_COLUMN == nodeType(*pNode)) { + return rewriteColToSelectValFunc((STranslateContext*)pContext, NULL, pNode); + } + return DEAL_RES_CONTINUE; +} + +static int32_t rewriteColsToSelectValFunc(STranslateContext* pCxt, SSelectStmt* pSelect) { + nodesRewriteExprs(pSelect->pProjectionList, rewriteColsToSelectValFuncImpl, pCxt); + if (TSDB_CODE_SUCCESS == pCxt->errCode && !pSelect->isDistinct) { + nodesRewriteExprs(pSelect->pOrderByList, rewriteColsToSelectValFuncImpl, pCxt); + } return pCxt->errCode; } @@ -758,11 +866,13 @@ typedef struct CheckAggColCoexistCxt { STranslateContext* pTranslateCxt; bool existAggFunc; bool existCol; + int32_t selectFuncNum; } CheckAggColCoexistCxt; static EDealRes doCheckAggColCoexist(SNode* pNode, void* pContext) { CheckAggColCoexistCxt* pCxt = (CheckAggColCoexistCxt*)pContext; - if (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsAggFunc(((SFunctionNode*)pNode)->funcId)) { + pCxt->selectFuncNum += isSelectFunc(pNode) ? 1 : 0; + if (isAggFunc(pNode)) { pCxt->existAggFunc = true; return DEAL_RES_IGNORE_CHILD; } @@ -781,7 +891,9 @@ static int32_t checkAggColCoexist(STranslateContext* pCxt, SSelectStmt* pSelect) if (!pSelect->isDistinct) { nodesWalkExprs(pSelect->pOrderByList, doCheckAggColCoexist, &cxt); } - if ((cxt.existAggFunc || NULL != pSelect->pWindow) && cxt.existCol) { + if (1 == cxt.selectFuncNum) { + return rewriteColsToSelectValFunc(pCxt, pSelect); + } else if ((cxt.selectFuncNum > 1 || cxt.existAggFunc || NULL != pSelect->pWindow) && cxt.existCol) { return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NOT_SINGLE_GROUP); } return TSDB_CODE_SUCCESS; @@ -990,19 +1102,6 @@ static SNode* createMultiResFunc(SFunctionNode* pSrcFunc, SExprNode* pExpr) { return (SNode*)pFunc; } -static int32_t findTable(STranslateContext* pCxt, const char* pTableAlias, STableNode** pOutput) { - SArray* pTables = taosArrayGetP(pCxt->pNsLevel, pCxt->currLevel); - size_t nums = taosArrayGetSize(pTables); - for (size_t i = 0; i < nums; ++i) { - STableNode* pTable = taosArrayGetP(pTables, i); - if (NULL == pTableAlias || 0 == strcmp(pTable->tableAlias, pTableAlias)) { - *pOutput = pTable; - return TSDB_CODE_SUCCESS; - } - } - return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_TABLE_NOT_EXIST, pTableAlias); -} - static int32_t createTableAllCols(STranslateContext* pCxt, SColumnNode* pCol, SNodeList** pOutput) { STableNode* pTable = NULL; int32_t code = findTable(pCxt, pCol->tableAlias, &pTable); @@ -1228,7 +1327,7 @@ static int32_t translateHaving(STranslateContext* pCxt, SSelectStmt* pSelect) { pCxt->currClause = SQL_CLAUSE_HAVING; int32_t code = translateExpr(pCxt, pSelect->pHaving); if (TSDB_CODE_SUCCESS == code) { - code = checkExprForGroupBy(pCxt, pSelect->pHaving); + code = checkExprForGroupBy(pCxt, &pSelect->pHaving); } return code; } @@ -1511,25 +1610,6 @@ static int32_t checkLimit(STranslateContext* pCxt, SSelectStmt* pSelect) { return TSDB_CODE_SUCCESS; } -static bool isCountStar(SFunctionNode* pFunc) { - if (1 != LIST_LENGTH(pFunc->pParameterList)) { - return false; - } - SNode* pPara = nodesListGetNode(pFunc->pParameterList, 0); - return (QUERY_NODE_COLUMN == nodeType(pPara) && 0 == strcmp(((SColumnNode*)pPara)->colName, "*")); -} - -// count(*) is rewritten as count(ts) for scannning optimization -static int32_t rewriteCountStar(STranslateContext* pCxt, SFunctionNode* pCount) { - SColumnNode* pCol = nodesListGetNode(pCount->pParameterList, 0); - STableNode* pTable = NULL; - int32_t code = findTable(pCxt, ('\0' == pCol->tableAlias[0] ? NULL : pCol->tableAlias), &pTable); - if (TSDB_CODE_SUCCESS == code && QUERY_NODE_REAL_TABLE == nodeType(pTable)) { - setColumnInfoBySchema((SRealTableNode*)pTable, ((SRealTableNode*)pTable)->pMeta->schema, false, pCol); - } - return code; -} - static int32_t createPrimaryKeyColByTable(STranslateContext* pCxt, STableNode* pTable, SNode** pPrimaryKey) { SColumnNode* pCol = nodesMakeNode(QUERY_NODE_COLUMN); if (NULL == pCol) { @@ -1553,33 +1633,22 @@ static int32_t createPrimaryKeyCol(STranslateContext* pCxt, SNode** pPrimaryKey) return code; } -static int32_t rewriteTimelineFunc(STranslateContext* pCxt, SFunctionNode* pFunc) { - SNode* pPrimaryKey = NULL; - int32_t code = createPrimaryKeyCol(pCxt, &pPrimaryKey); - if (TSDB_CODE_SUCCESS == code) { - code = nodesListMakeStrictAppend(&pFunc->pParameterList, pPrimaryKey); - } - return code; -} - -EDealRes rewriteFuncForSelectImpl(SNode* pNode, void* pContext) { - if (QUERY_NODE_FUNCTION == nodeType(pNode)) { - STranslateContext* pCxt = pContext; - SFunctionNode* pFunc = (SFunctionNode*)pNode; - if (isCountStar(pFunc)) { - pCxt->errCode = rewriteCountStar(pCxt, pFunc); - } else if (fmIsTimelineFunc(pFunc->funcId)) { - pCxt->errCode = rewriteTimelineFunc(pCxt, pFunc); - } - if (TSDB_CODE_SUCCESS != pCxt->errCode) { - return DEAL_RES_ERROR; +static EDealRes rewriteTimelineFuncImpl(SNode* pNode, void* pContext) { + STranslateContext* pCxt = pContext; + if (isTimelineFunc(pNode)) { + SFunctionNode* pFunc = (SFunctionNode*)pNode; + SNode* pPrimaryKey = NULL; + pCxt->errCode = createPrimaryKeyCol(pCxt, &pPrimaryKey); + if (TSDB_CODE_SUCCESS == pCxt->errCode) { + pCxt->errCode = nodesListMakeStrictAppend(&pFunc->pParameterList, pPrimaryKey); } + return TSDB_CODE_SUCCESS == pCxt->errCode ? DEAL_RES_IGNORE_CHILD : DEAL_RES_ERROR; } return DEAL_RES_CONTINUE; } -static int32_t rewriteFuncForSelect(STranslateContext* pCxt, SSelectStmt* pSelect) { - nodesWalkSelectStmt(pSelect, SQL_CLAUSE_FROM, rewriteFuncForSelectImpl, pCxt); +static int32_t rewriteTimelineFunc(STranslateContext* pCxt, SSelectStmt* pSelect) { + nodesWalkSelectStmt(pSelect, SQL_CLAUSE_FROM, rewriteTimelineFuncImpl, pCxt); return pCxt->errCode; } @@ -1614,7 +1683,7 @@ static int32_t translateSelect(STranslateContext* pCxt, SSelectStmt* pSelect) { code = checkLimit(pCxt, pSelect); } if (TSDB_CODE_SUCCESS == code) { - code = rewriteFuncForSelect(pCxt, pSelect); + code = rewriteTimelineFunc(pCxt, pSelect); } return code; } diff --git a/source/libs/parser/test/mockCatalogService.cpp b/source/libs/parser/test/mockCatalogService.cpp index 2c85bfdf2e5f71674cf329c478572fec4f0119b4..f86cecb9e3399bf6b5b55c59adcc6b99e1950468 100644 --- a/source/libs/parser/test/mockCatalogService.cpp +++ b/source/libs/parser/test/mockCatalogService.cpp @@ -103,7 +103,6 @@ class MockCatalogServiceImpl { const char* tname = tNameGetTableName(pTableName); int32_t code = copyTableSchemaMeta(db, tname, &table); if (TSDB_CODE_SUCCESS != code) { - std::cout << "db : " << db << ", table :" << tname << std::endl; return code; } *pTableMeta = table.release(); diff --git a/source/libs/parser/test/parSelectTest.cpp b/source/libs/parser/test/parSelectTest.cpp index c80a0341d2fa1dcbff9836637fb02b1f2cc39dc5..5a385ba25e10677f77d88fb74cbd440b2cb21a8a 100644 --- a/source/libs/parser/test/parSelectTest.cpp +++ b/source/libs/parser/test/parSelectTest.cpp @@ -67,49 +67,72 @@ TEST_F(ParserSelectTest, condition) { TEST_F(ParserSelectTest, pseudoColumn) { useDb("root", "test"); - run("SELECT _wstartts, _wendts, COUNT(*) FROM t1 INTERVAL(10s)"); + run("SELECT _WSTARTTS, _WENDTS, COUNT(*) FROM t1 INTERVAL(10s)"); } TEST_F(ParserSelectTest, multiResFunc) { useDb("root", "test"); - run("SELECT last(*), first(*), last_row(*) FROM t1"); + run("SELECT LAST(*), FIRST(*), LAST_ROW(*) FROM t1"); - run("SELECT last(c1, c2), first(t1.*), last_row(c3) FROM t1"); + run("SELECT LAST(c1, c2), FIRST(t1.*), LAST_ROW(c3) FROM t1"); - run("SELECT last(t2.*), first(t1.c1, t2.*), last_row(t1.*, t2.*) FROM st1s1 t1, st1s2 t2 WHERE t1.ts = t2.ts"); + run("SELECT LAST(t2.*), FIRST(t1.c1, t2.*), LAST_ROW(t1.*, t2.*) FROM st1s1 t1, st1s2 t2 WHERE t1.ts = t2.ts"); } TEST_F(ParserSelectTest, timelineFunc) { useDb("root", "test"); - run("SELECT last(*), first(*) FROM t1"); + run("SELECT LAST(*), FIRST(*) FROM t1"); - run("SELECT last(*), first(*) FROM t1 group by c1"); + run("SELECT FIRST(ts), FIRST(c1), FIRST(c2), FIRST(c3) FROM t1"); - run("SELECT last(*), first(*) FROM t1 INTERVAL(10s)"); + run("SELECT LAST(*), FIRST(*) FROM t1 GROUP BY c1"); + + run("SELECT LAST(*), FIRST(*) FROM t1 INTERVAL(10s)"); run("SELECT diff(c1) FROM t1"); } +TEST_F(ParserSelectTest, selectFunc) { + useDb("root", "test"); + + // select function + run("SELECT MAX(c1), MIN(c1) FROM t1"); + // select function for GROUP BY clause + run("SELECT MAX(c1), MIN(c1) FROM t1 GROUP BY c1"); + // select function for INTERVAL clause + run("SELECT MAX(c1), MIN(c1) FROM t1 INTERVAL(10s)"); + // select function along with the columns of select row + run("SELECT MAX(c1), c2 FROM t1"); + run("SELECT MAX(c1), t1.* FROM t1"); + // select function along with the columns of select row, and with GROUP BY clause + run("SELECT MAX(c1), c2 FROM t1 GROUP BY c3"); + run("SELECT MAX(c1), t1.* FROM t1 GROUP BY c3"); + // select function along with the columns of select row, and with window clause + run("SELECT MAX(c1), c2 FROM t1 INTERVAL(10s)"); + run("SELECT MAX(c1), c2 FROM t1 SESSION(ts, 10s)"); + run("SELECT MAX(c1), c2 FROM t1 STATE_WINDOW(c3)"); +} + TEST_F(ParserSelectTest, clause) { useDb("root", "test"); - // group by clause + // GROUP BY clause run("SELECT COUNT(*) cnt FROM t1 WHERE c1 > 0"); - run("SELECT COUNT(*), c2 cnt FROM t1 WHERE c1 > 0 group by c2"); + run("SELECT COUNT(*), c2 cnt FROM t1 WHERE c1 > 0 GROUP BY c2"); - run("SELECT COUNT(*) cnt FROM t1 WHERE c1 > 0 group by c2 having COUNT(c1) > 10"); + run("SELECT COUNT(*) cnt FROM t1 WHERE c1 > 0 GROUP BY c2 having COUNT(c1) > 10"); - run("SELECT COUNT(*), c1, c2 + 10, c1 + c2 cnt FROM t1 WHERE c1 > 0 group by c2, c1"); + run("SELECT COUNT(*), c1, c2 + 10, c1 + c2 cnt FROM t1 WHERE c1 > 0 GROUP BY c2, c1"); - run("SELECT COUNT(*), c1 + 10, c2 cnt FROM t1 WHERE c1 > 0 group by c1 + 10, c2"); + run("SELECT COUNT(*), c1 + 10, c2 cnt FROM t1 WHERE c1 > 0 GROUP BY c1 + 10, c2"); // order by clause - run("SELECT COUNT(*) cnt FROM t1 WHERE c1 > 0 group by c2 order by cnt"); + run("SELECT COUNT(*) cnt FROM t1 WHERE c1 > 0 GROUP BY c2 order by cnt"); - run("SELECT COUNT(*) cnt FROM t1 WHERE c1 > 0 group by c2 order by 1"); + run("SELECT COUNT(*) cnt FROM t1 WHERE c1 > 0 GROUP BY c2 order by 1"); // distinct clause // run("SELECT distinct c1, c2 FROM t1 WHERE c1 > 0 order by c1"); @@ -118,7 +141,7 @@ TEST_F(ParserSelectTest, clause) { // run("SELECT distinct c1 + 10 cc1, c2 cc2 FROM t1 WHERE c1 > 0 order by cc1, c2"); - // run("SELECT distinct COUNT(c2) FROM t1 WHERE c1 > 0 group by c1 order by COUNT(c2)"); + // run("SELECT distinct COUNT(c2) FROM t1 WHERE c1 > 0 GROUP BY c1 order by COUNT(c2)"); } // INTERVAL(interval_val [, interval_offset]) [SLIDING (sliding_val)] [FILL(fill_mod_and_val)] @@ -137,6 +160,14 @@ TEST_F(ParserSelectTest, interval) { "INTERVAL(10s) FILL(NONE)"); } +TEST_F(ParserSelectTest, intervalSemanticCheck) { + useDb("root", "test"); + + run("SELECT c1 FROM t1 INTERVAL(10s)", TSDB_CODE_PAR_NOT_SINGLE_GROUP, PARSER_STAGE_TRANSLATE); + run("SELECT DISTINCT c1, c2 FROM t1 WHERE c1 > 3 INTERVAL(1d) FILL(NEXT)", TSDB_CODE_PAR_INVALID_FILL_TIME_RANGE, + PARSER_STAGE_TRANSLATE); +} + TEST_F(ParserSelectTest, semanticError) { useDb("root", "test"); @@ -164,7 +195,7 @@ TEST_F(ParserSelectTest, semanticError) { run("SELECT c2 FROM t1 WHERE COUNT(*) > 0", TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION, PARSER_STAGE_TRANSLATE); - run("SELECT c2 FROM t1 group by COUNT(*)", TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION, PARSER_STAGE_TRANSLATE); + run("SELECT c2 FROM t1 GROUP BY COUNT(*)", TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION, PARSER_STAGE_TRANSLATE); // TSDB_CODE_PAR_WRONG_NUMBER_OF_SELECT run("SELECT c2 FROM t1 order by 0", TSDB_CODE_PAR_WRONG_NUMBER_OF_SELECT, PARSER_STAGE_TRANSLATE); @@ -174,13 +205,13 @@ TEST_F(ParserSelectTest, semanticError) { // TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION run("SELECT COUNT(*) cnt FROM t1 having c1 > 0", TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION, PARSER_STAGE_TRANSLATE); - run("SELECT COUNT(*) cnt FROM t1 group by c2 having c1 > 0", TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION, + run("SELECT COUNT(*) cnt FROM t1 GROUP BY c2 having c1 > 0", TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION, PARSER_STAGE_TRANSLATE); - run("SELECT COUNT(*), c1 cnt FROM t1 group by c2 having c2 > 0", TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION, + run("SELECT COUNT(*), c1 cnt FROM t1 GROUP BY c2 having c2 > 0", TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION, PARSER_STAGE_TRANSLATE); - run("SELECT COUNT(*) cnt FROM t1 group by c2 having c2 > 0 order by c1", TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION, + run("SELECT COUNT(*) cnt FROM t1 GROUP BY c2 having c2 > 0 order by c1", TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION, PARSER_STAGE_TRANSLATE); // TSDB_CODE_PAR_NOT_SINGLE_GROUP diff --git a/source/libs/planner/test/planGroupByTest.cpp b/source/libs/planner/test/planGroupByTest.cpp index 05ffe41fe7cc69b916dba37059bc71c754b722ad..9ca1001f4cb24224d9dd223e366c9eed83db4fbe 100644 --- a/source/libs/planner/test/planGroupByTest.cpp +++ b/source/libs/planner/test/planGroupByTest.cpp @@ -23,30 +23,45 @@ class PlanGroupByTest : public PlannerTestBase {}; TEST_F(PlanGroupByTest, basic) { useDb("root", "test"); - run("select count(*) from t1"); + run("SELECT COUNT(*) FROM t1"); - run("select c1, max(c3), min(c3), count(*) from t1 group by c1"); + run("SELECT c1, MAX(c3), MIN(c3), COUNT(*) FROM t1 GROUP BY c1"); - run("select c1 + c3, c1 + count(*) from t1 where c2 = 'abc' group by c1, c3"); + run("SELECT c1 + c3, c1 + COUNT(*) FROM t1 WHERE c2 = 'abc' GROUP BY c1, c3"); - run("select c1 + c3, sum(c4 * c5) from t1 where concat(c2, 'wwww') = 'abcwww' group by c1 + c3"); + run("SELECT c1 + c3, SUM(c4 * c5) FROM t1 WHERE CONCAT(c2, 'wwww') = 'abcwww' GROUP BY c1 + c3"); - run("select sum(ceil(c1)) from t1 group by ceil(c1)"); + run("SELECT SUM(CEIL(c1)) FROM t1 GROUP BY CEIL(c1)"); } TEST_F(PlanGroupByTest, withOrderBy) { useDb("root", "test"); - // order by aggfunc - run("select count(*), sum(c1) from t1 order by sum(c1)"); - // order by alias of aggfunc - // run("select count(*), sum(c1) a from t1 order by a"); + // ORDER BY aggfunc + run("SELECT COUNT(*), SUM(c1) FROM t1 ORDER BY SUM(c1)"); + // ORDER BY alias of aggfunc + // run("SELECT COUNT(*), SUM(c1) a FROM t1 ORDER BY a"); } TEST_F(PlanGroupByTest, aggFunc) { useDb("root", "test"); - run("select last(*), first(*) from t1"); + run("SELECT LAST(*), FIRST(*) FROM t1"); - run("select last(*), first(*) from t1 group by c1"); + run("SELECT LAST(*), FIRST(*) FROM t1 GROUP BY c1"); +} + +TEST_F(PlanGroupByTest, selectFunc) { + useDb("root", "test"); + + // select function + run("SELECT MAX(c1), MIN(c1) FROM t1"); + // select function for GROUP BY clause + run("SELECT MAX(c1), MIN(c1) FROM t1 GROUP BY c1"); + // select function along with the columns of select row + run("SELECT MAX(c1), c2 FROM t1"); + run("SELECT MAX(c1), t1.* FROM t1"); + // select function along with the columns of select row, and with GROUP BY clause + run("SELECT MAX(c1), c2 FROM t1 GROUP BY c3"); + run("SELECT MAX(c1), t1.* FROM t1 GROUP BY c3"); } diff --git a/source/libs/planner/test/planIntervalTest.cpp b/source/libs/planner/test/planIntervalTest.cpp index f1bcd747c03b2bf039570250bde8f4b950526880..c9bae46ca9438977f4078ceac82e6c7c4b3c680e 100644 --- a/source/libs/planner/test/planIntervalTest.cpp +++ b/source/libs/planner/test/planIntervalTest.cpp @@ -42,3 +42,12 @@ TEST_F(PlanIntervalTest, fill) { "WHERE ts > TIMESTAMP '2022-04-01 00:00:00' and ts < TIMESTAMP '2022-04-30 23:59:59' " "INTERVAL(10s) FILL(VALUE, 10, 20)"); } + +TEST_F(PlanIntervalTest, selectFunc) { + useDb("root", "test"); + + // select function for INTERVAL clause + run("SELECT MAX(c1), MIN(c1) FROM t1 INTERVAL(10s)"); + // select function along with the columns of select row, and with INTERVAL clause + run("SELECT MAX(c1), c2 FROM t1 INTERVAL(10s)"); +} \ No newline at end of file diff --git a/source/libs/planner/test/planSessionTest.cpp b/source/libs/planner/test/planSessionTest.cpp index 3ec9c4c387a2e0b21ed731e2ae2ba0d0a38e20f3..7d5d82692557207ad032e4a9172c52da86b75f44 100644 --- a/source/libs/planner/test/planSessionTest.cpp +++ b/source/libs/planner/test/planSessionTest.cpp @@ -25,3 +25,12 @@ TEST_F(PlanSessionTest, basic) { run("select count(*) from t1 session(ts, 10s)"); } + +TEST_F(PlanSessionTest, selectFunc) { + useDb("root", "test"); + + // select function for SESSION clause + run("SELECT MAX(c1), MIN(c1) FROM t1 SESSION(ts, 10s)"); + // select function along with the columns of select row, and with SESSION clause + run("SELECT MAX(c1), c2 FROM t1 SESSION(ts, 10s)"); +} diff --git a/source/libs/planner/test/planStateTest.cpp b/source/libs/planner/test/planStateTest.cpp index 46988d67066659ea284610858ef3662d7c6d6a3b..83c9621916a3a080d578d29cf1a1abd6f0dc94d9 100644 --- a/source/libs/planner/test/planStateTest.cpp +++ b/source/libs/planner/test/planStateTest.cpp @@ -31,3 +31,12 @@ TEST_F(PlanStateTest, stateExpr) { run("select count(*) from t1 state_window(c1 + 10)"); } + +TEST_F(PlanStateTest, selectFunc) { + useDb("root", "test"); + + // select function for STATE_WINDOW clause + run("SELECT MAX(c1), MIN(c1) FROM t1 STATE_WINDOW(c3)"); + // select function along with the columns of select row, and with STATE_WINDOW clause + run("SELECT MAX(c1), c2 FROM t1 STATE_WINDOW(c3)"); +} diff --git a/tests/system-test/2-query/distinct.py b/tests/system-test/2-query/distinct.py index a82f3a6f59f243ffc6eafe6f36b1bdcdb9b9e12f..937ff78c71eba9ec617ddd9a05b04f22e2229820 100644 --- a/tests/system-test/2-query/distinct.py +++ b/tests/system-test/2-query/distinct.py @@ -101,7 +101,7 @@ class TDTestCase: # tdSql.error("select distinct c1, ts from stb1 group by c2") tdSql.error("select distinct c1, ts from t1 group by c2") # tdSql.error("select distinct c1, max(c2) from stb1 ") - tdSql.error("select distinct c1, max(c2) from t1 ") + # tdSql.error("select distinct c1, max(c2) from t1 ") # tdSql.error("select max(c2), distinct c1 from stb1 ") tdSql.error("select max(c2), distinct c1 from t1 ") # tdSql.error("select distinct c1, c2 from stb1 where c1 > 3 group by t0")