From f99c01e66e756e28f03c425955c5697fd62f4455 Mon Sep 17 00:00:00 2001 From: Xiaoyu Wang Date: Sat, 30 Apr 2022 19:11:25 +0800 Subject: [PATCH 1/6] feat: sql command 'select max(c1), c2 from t' --- source/libs/parser/src/parTranslater.c | 184 ++++++++++------------ source/libs/parser/test/parSelectTest.cpp | 46 ++++-- 2 files changed, 116 insertions(+), 114 deletions(-) diff --git a/source/libs/parser/src/parTranslater.c b/source/libs/parser/src/parTranslater.c index d9b250998e..1e030fdc45 100644 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -624,6 +624,70 @@ static EDealRes haveAggFunction(SNode* pNode, void* pContext) { 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 int32_t createPrimaryKeyColByTable(STranslateContext* pCxt, STableNode* pTable, SNode** pPrimaryKey) { + SColumnNode* pCol = nodesMakeNode(QUERY_NODE_COLUMN); + if (NULL == pCol) { + return TSDB_CODE_OUT_OF_MEMORY; + } + if (QUERY_NODE_REAL_TABLE == nodeType(pTable)) { + setColumnInfoBySchema((SRealTableNode*)pTable, ((SRealTableNode*)pTable)->pMeta->schema, false, pCol); + } else { + // todo + } + *pPrimaryKey = (SNode*)pCol; + return TSDB_CODE_SUCCESS; +} + +static int32_t createPrimaryKeyCol(STranslateContext* pCxt, SNode** pPrimaryKey) { + STableNode* pTable = NULL; + int32_t code = findTable(pCxt, NULL, &pTable); + if (TSDB_CODE_SUCCESS == code) { + code = createPrimaryKeyColByTable(pCxt, pTable, 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; +} + static EDealRes translateFunction(STranslateContext* pCxt, SFunctionNode* pFunc) { SFmGetFuncInfoParam param = {.pCtg = pCxt->pParseCxt->pCatalog, .pRpc = pCxt->pParseCxt->pTransporter, @@ -631,10 +695,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 +704,17 @@ 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; + if (TSDB_CODE_SUCCESS == pCxt->errCode && fmIsTimelineFunc(pFunc->funcId)) { + pCxt->errCode = rewriteTimelineFunc(pCxt, pFunc); + } + return TSDB_CODE_SUCCESS == pCxt->errCode ? DEAL_RES_CONTINUE : DEAL_RES_ERROR; } static EDealRes translateExprSubquery(STranslateContext* pCxt, SNode* pNode) { @@ -691,7 +758,13 @@ static int32_t translateExprList(STranslateContext* pCxt, SNodeList* pList) { return pCxt->errCode; } -static bool isAliasColumn(SColumnNode* pCol) { return ('\0' == pCol->tableAlias[0]); } +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 isDistinctOrderBy(STranslateContext* pCxt) { return (SQL_CLAUSE_ORDER_BY == pCxt->currClause && pCxt->pCurrStmt->isDistinct); @@ -720,11 +793,10 @@ static int32_t getGroupByErrorCode(STranslateContext* pCxt) { static EDealRes doCheckExprForGroupBy(SNode* pNode, void* pContext) { STranslateContext* pCxt = (STranslateContext*)pContext; - if (!nodesIsExprNode(pNode) || (QUERY_NODE_COLUMN == nodeType(pNode) && isAliasColumn((SColumnNode*)pNode))) { + if (!nodesIsExprNode(pNode) || isAliasColumn(pNode)) { return DEAL_RES_CONTINUE; } - if (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsAggFunc(((SFunctionNode*)pNode)->funcId) && - !isDistinctOrderBy(pCxt)) { + if (isAggFunc(pNode) && !isDistinctOrderBy(pCxt)) { return DEAL_RES_IGNORE_CHILD; } SNode* pGroupNode; @@ -733,9 +805,7 @@ static EDealRes doCheckExprForGroupBy(SNode* pNode, void* pContext) { return DEAL_RES_IGNORE_CHILD; } } - if (QUERY_NODE_COLUMN == nodeType(pNode) || - (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsAggFunc(((SFunctionNode*)pNode)->funcId) && - isDistinctOrderBy(pCxt))) { + if (QUERY_NODE_COLUMN == nodeType(pNode) || (isAggFunc(pNode) && isDistinctOrderBy(pCxt))) { return generateDealNodeErrMsg(pCxt, getGroupByErrorCode(pCxt)); } return DEAL_RES_CONTINUE; @@ -990,19 +1060,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); @@ -1511,78 +1568,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) { - return TSDB_CODE_OUT_OF_MEMORY; - } - if (QUERY_NODE_REAL_TABLE == nodeType(pTable)) { - setColumnInfoBySchema((SRealTableNode*)pTable, ((SRealTableNode*)pTable)->pMeta->schema, false, pCol); - } else { - // todo - } - *pPrimaryKey = (SNode*)pCol; - return TSDB_CODE_SUCCESS; -} - -static int32_t createPrimaryKeyCol(STranslateContext* pCxt, SNode** pPrimaryKey) { - STableNode* pTable = NULL; - int32_t code = findTable(pCxt, NULL, &pTable); - if (TSDB_CODE_SUCCESS == code) { - code = createPrimaryKeyColByTable(pCxt, pTable, 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; - } - } - return DEAL_RES_CONTINUE; -} - -static int32_t rewriteFuncForSelect(STranslateContext* pCxt, SSelectStmt* pSelect) { - nodesWalkSelectStmt(pSelect, SQL_CLAUSE_FROM, rewriteFuncForSelectImpl, pCxt); - return pCxt->errCode; -} - static int32_t translateSelect(STranslateContext* pCxt, SSelectStmt* pSelect) { pCxt->pCurrStmt = pSelect; int32_t code = translateFrom(pCxt, pSelect); @@ -1613,9 +1598,6 @@ static int32_t translateSelect(STranslateContext* pCxt, SSelectStmt* pSelect) { if (TSDB_CODE_SUCCESS == code) { code = checkLimit(pCxt, pSelect); } - if (TSDB_CODE_SUCCESS == code) { - code = rewriteFuncForSelect(pCxt, pSelect); - } return code; } diff --git a/source/libs/parser/test/parSelectTest.cpp b/source/libs/parser/test/parSelectTest.cpp index c80a0341d2..19105d4360 100644 --- a/source/libs/parser/test/parSelectTest.cpp +++ b/source/libs/parser/test/parSelectTest.cpp @@ -85,31 +85,45 @@ TEST_F(ParserSelectTest, timelineFunc) { run("SELECT last(*), first(*) FROM t1"); - run("SELECT last(*), first(*) FROM t1 group by c1"); + 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), * FROM t1"); + run("SELECT MAX(c1), t1.* FROM t1"); +} + 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 +132,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 +151,12 @@ TEST_F(ParserSelectTest, interval) { "INTERVAL(10s) FILL(NONE)"); } +TEST_F(ParserSelectTest, intervalSemanticCheck) { + useDb("root", "test"); + + run("SELECT c1 FROM t1 INTERVAL(10s)"); +} + TEST_F(ParserSelectTest, semanticError) { useDb("root", "test"); @@ -164,7 +184,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 +194,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 From 3b4f9b91cf36b5635a53efb6a941e5bb1778964b Mon Sep 17 00:00:00 2001 From: Xiaoyu Wang Date: Sun, 1 May 2022 08:38:17 +0800 Subject: [PATCH 2/6] feat: sql commadn 'select max(c1), c2 from t2' --- include/libs/function/functionMgt.h | 4 + source/libs/function/inc/functionMgtInt.h | 1 + source/libs/function/src/builtins.c | 19 ++- source/libs/function/src/functionMgt.c | 2 + source/libs/parser/src/parTranslater.c | 127 ++++++++++++++---- .../libs/parser/test/mockCatalogService.cpp | 1 - source/libs/parser/test/parSelectTest.cpp | 11 +- source/libs/planner/test/planGroupByTest.cpp | 37 +++-- source/libs/planner/test/planIntervalTest.cpp | 9 ++ source/libs/planner/test/planSessionTest.cpp | 9 ++ source/libs/planner/test/planStateTest.cpp | 9 ++ 11 files changed, 186 insertions(+), 43 deletions(-) diff --git a/include/libs/function/functionMgt.h b/include/libs/function/functionMgt.h index f6304401fc..1fa9db9927 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 e19a332e66..3869a5d7b2 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 8f3c88900d..bd2fb9dca1 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 b505f2e8ec..f8ef0f7d20 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 1e030fdc45..82f51f70c1 100644 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -256,6 +256,22 @@ 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 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,7 +633,7 @@ 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; } @@ -758,18 +774,6 @@ static int32_t translateExprList(STranslateContext* pCxt, SNodeList* pList) { return pCxt->errCode; } -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 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; @@ -791,28 +795,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) || isAliasColumn(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 (isAggFunc(pNode) && !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) || (isAggFunc(pNode) && 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; } @@ -820,7 +867,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; } @@ -828,11 +897,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; } @@ -851,7 +922,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; @@ -1285,7 +1358,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; } diff --git a/source/libs/parser/test/mockCatalogService.cpp b/source/libs/parser/test/mockCatalogService.cpp index 2c85bfdf2e..f86cecb9e3 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 19105d4360..58e517d571 100644 --- a/source/libs/parser/test/parSelectTest.cpp +++ b/source/libs/parser/test/parSelectTest.cpp @@ -94,6 +94,7 @@ TEST_F(ParserSelectTest, timelineFunc) { TEST_F(ParserSelectTest, selectFunc) { useDb("root", "test"); + // select function run("SELECT MAX(c1), MIN(c1) FROM t1"); // select function for GROUP BY clause @@ -102,8 +103,14 @@ TEST_F(ParserSelectTest, selectFunc) { 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), * 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) { @@ -154,7 +161,7 @@ TEST_F(ParserSelectTest, interval) { TEST_F(ParserSelectTest, intervalSemanticCheck) { useDb("root", "test"); - run("SELECT c1 FROM t1 INTERVAL(10s)"); + run("SELECT c1 FROM t1 INTERVAL(10s)", TSDB_CODE_PAR_NOT_SINGLE_GROUP, PARSER_STAGE_TRANSLATE); } TEST_F(ParserSelectTest, semanticError) { diff --git a/source/libs/planner/test/planGroupByTest.cpp b/source/libs/planner/test/planGroupByTest.cpp index 05ffe41fe7..9ca1001f4c 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 f1bcd747c0..c9bae46ca9 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 3ec9c4c387..7d5d826925 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 46988d6706..83c9621916 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)"); +} From afcf0c8a63110d3bd6c3a41ad4a96e5cf04e14b0 Mon Sep 17 00:00:00 2001 From: Xiaoyu Wang Date: Sun, 1 May 2022 08:56:59 +0800 Subject: [PATCH 3/6] feat: sql commadn 'select max(c1), c2 from t' --- tests/system-test/2-query/distinct.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system-test/2-query/distinct.py b/tests/system-test/2-query/distinct.py index a82f3a6f59..63794a5022 100644 --- a/tests/system-test/2-query/distinct.py +++ b/tests/system-test/2-query/distinct.py @@ -111,7 +111,7 @@ class TDTestCase: # tdSql.error("select distinct c1, c2 from stb1 where c1 > 3 interval(1d) fill(next)") tdSql.error("select distinct c1, c2 from t1 where c1 > 3 interval(1d) fill(next)") # tdSql.error("select distinct c1, c2 from stb1 where ts > now-10d and ts < now interval(1d) fill(next)") - tdSql.error("select distinct c1, c2 from t1 where ts > now-10d and ts < now interval(1d) fill(next)") + # tdSql.error("select distinct c1, c2 from t1 where ts > now-10d and ts < now interval(1d) fill(next)") # tdSql.error("select distinct c1, c2 from stb1 where c1 > 3 slimit 1") # tdSql.error("select distinct c1, c2 from t1 where c1 > 3 slimit 1") # tdSql.query(f"select distinct c1, c2 from stb1 where c1 between {tbnum-2} and {tbnum} ") From d6cf688368e23d1b87cfcea44612b2eaf44953d8 Mon Sep 17 00:00:00 2001 From: Xiaoyu Wang Date: Sun, 1 May 2022 09:21:36 +0800 Subject: [PATCH 4/6] feat: sql commadn 'select max(c1), c2 from t' --- source/libs/parser/test/parSelectTest.cpp | 2 ++ tests/system-test/2-query/distinct.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/source/libs/parser/test/parSelectTest.cpp b/source/libs/parser/test/parSelectTest.cpp index 58e517d571..4e0d2851f6 100644 --- a/source/libs/parser/test/parSelectTest.cpp +++ b/source/libs/parser/test/parSelectTest.cpp @@ -162,6 +162,8 @@ 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) { diff --git a/tests/system-test/2-query/distinct.py b/tests/system-test/2-query/distinct.py index 63794a5022..a82f3a6f59 100644 --- a/tests/system-test/2-query/distinct.py +++ b/tests/system-test/2-query/distinct.py @@ -111,7 +111,7 @@ class TDTestCase: # tdSql.error("select distinct c1, c2 from stb1 where c1 > 3 interval(1d) fill(next)") tdSql.error("select distinct c1, c2 from t1 where c1 > 3 interval(1d) fill(next)") # tdSql.error("select distinct c1, c2 from stb1 where ts > now-10d and ts < now interval(1d) fill(next)") - # tdSql.error("select distinct c1, c2 from t1 where ts > now-10d and ts < now interval(1d) fill(next)") + tdSql.error("select distinct c1, c2 from t1 where ts > now-10d and ts < now interval(1d) fill(next)") # tdSql.error("select distinct c1, c2 from stb1 where c1 > 3 slimit 1") # tdSql.error("select distinct c1, c2 from t1 where c1 > 3 slimit 1") # tdSql.query(f"select distinct c1, c2 from stb1 where c1 between {tbnum-2} and {tbnum} ") From d97f1cc3887f658abc0ea826d6d421917dc25f52 Mon Sep 17 00:00:00 2001 From: Xiaoyu Wang Date: Sun, 1 May 2022 09:53:52 +0800 Subject: [PATCH 5/6] feat: sql commadn 'select max(c1), c2 from t' --- source/libs/parser/src/parTranslater.c | 84 +++++++++++++---------- source/libs/parser/test/parSelectTest.cpp | 16 +++-- tests/system-test/2-query/distinct.py | 4 +- 3 files changed, 60 insertions(+), 44 deletions(-) diff --git a/source/libs/parser/src/parTranslater.c b/source/libs/parser/src/parTranslater.c index 82f51f70c1..4e2d75a79f 100644 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -268,6 +268,10 @@ 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); } @@ -672,38 +676,6 @@ static int32_t rewriteCountStar(STranslateContext* pCxt, SFunctionNode* pCount) return code; } -static int32_t createPrimaryKeyColByTable(STranslateContext* pCxt, STableNode* pTable, SNode** pPrimaryKey) { - SColumnNode* pCol = nodesMakeNode(QUERY_NODE_COLUMN); - if (NULL == pCol) { - return TSDB_CODE_OUT_OF_MEMORY; - } - if (QUERY_NODE_REAL_TABLE == nodeType(pTable)) { - setColumnInfoBySchema((SRealTableNode*)pTable, ((SRealTableNode*)pTable)->pMeta->schema, false, pCol); - } else { - // todo - } - *pPrimaryKey = (SNode*)pCol; - return TSDB_CODE_SUCCESS; -} - -static int32_t createPrimaryKeyCol(STranslateContext* pCxt, SNode** pPrimaryKey) { - STableNode* pTable = NULL; - int32_t code = findTable(pCxt, NULL, &pTable); - if (TSDB_CODE_SUCCESS == code) { - code = createPrimaryKeyColByTable(pCxt, pTable, 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; -} - static EDealRes translateFunction(STranslateContext* pCxt, SFunctionNode* pFunc) { SFmGetFuncInfoParam param = {.pCtg = pCxt->pParseCxt->pCatalog, .pRpc = pCxt->pParseCxt->pTransporter, @@ -727,9 +699,6 @@ static EDealRes translateFunction(STranslateContext* pCxt, SFunctionNode* pFunc) pCxt->errCode = rewriteCountStar(pCxt, pFunc); } } - if (TSDB_CODE_SUCCESS == pCxt->errCode && fmIsTimelineFunc(pFunc->funcId)) { - pCxt->errCode = rewriteTimelineFunc(pCxt, pFunc); - } return TSDB_CODE_SUCCESS == pCxt->errCode ? DEAL_RES_CONTINUE : DEAL_RES_ERROR; } @@ -1641,6 +1610,48 @@ static int32_t checkLimit(STranslateContext* pCxt, SSelectStmt* pSelect) { return TSDB_CODE_SUCCESS; } +static int32_t createPrimaryKeyColByTable(STranslateContext* pCxt, STableNode* pTable, SNode** pPrimaryKey) { + SColumnNode* pCol = nodesMakeNode(QUERY_NODE_COLUMN); + if (NULL == pCol) { + return TSDB_CODE_OUT_OF_MEMORY; + } + if (QUERY_NODE_REAL_TABLE == nodeType(pTable)) { + setColumnInfoBySchema((SRealTableNode*)pTable, ((SRealTableNode*)pTable)->pMeta->schema, false, pCol); + } else { + // todo + } + *pPrimaryKey = (SNode*)pCol; + return TSDB_CODE_SUCCESS; +} + +static int32_t createPrimaryKeyCol(STranslateContext* pCxt, SNode** pPrimaryKey) { + STableNode* pTable = NULL; + int32_t code = findTable(pCxt, NULL, &pTable); + if (TSDB_CODE_SUCCESS == code) { + code = createPrimaryKeyColByTable(pCxt, pTable, pPrimaryKey); + } + return code; +} + +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 rewriteTimelineFunc(STranslateContext* pCxt, SSelectStmt* pSelect) { + nodesWalkSelectStmt(pSelect, SQL_CLAUSE_FROM, rewriteTimelineFuncImpl, pCxt); + return pCxt->errCode; +} + static int32_t translateSelect(STranslateContext* pCxt, SSelectStmt* pSelect) { pCxt->pCurrStmt = pSelect; int32_t code = translateFrom(pCxt, pSelect); @@ -1671,6 +1682,9 @@ static int32_t translateSelect(STranslateContext* pCxt, SSelectStmt* pSelect) { if (TSDB_CODE_SUCCESS == code) { code = checkLimit(pCxt, pSelect); } + if (TSDB_CODE_SUCCESS == code) { + code = rewriteTimelineFunc(pCxt, pSelect); + } return code; } diff --git a/source/libs/parser/test/parSelectTest.cpp b/source/libs/parser/test/parSelectTest.cpp index 4e0d2851f6..5a385ba25e 100644 --- a/source/libs/parser/test/parSelectTest.cpp +++ b/source/libs/parser/test/parSelectTest.cpp @@ -67,27 +67,29 @@ 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"); } diff --git a/tests/system-test/2-query/distinct.py b/tests/system-test/2-query/distinct.py index a82f3a6f59..8f27fe4db6 100644 --- a/tests/system-test/2-query/distinct.py +++ b/tests/system-test/2-query/distinct.py @@ -109,9 +109,9 @@ class TDTestCase: # tdSql.error("select distinct c1, c2 from stb1 where c1 > 3 interval(1d) ") tdSql.error("select distinct c1, c2 from t1 where c1 > 3 interval(1d) ") # tdSql.error("select distinct c1, c2 from stb1 where c1 > 3 interval(1d) fill(next)") - tdSql.error("select distinct c1, c2 from t1 where c1 > 3 interval(1d) fill(next)") + # tdSql.error("select distinct c1, c2 from t1 where c1 > 3 interval(1d) fill(next)") # tdSql.error("select distinct c1, c2 from stb1 where ts > now-10d and ts < now interval(1d) fill(next)") - tdSql.error("select distinct c1, c2 from t1 where ts > now-10d and ts < now interval(1d) fill(next)") + # tdSql.error("select distinct c1, c2 from t1 where ts > now-10d and ts < now interval(1d) fill(next)") # tdSql.error("select distinct c1, c2 from stb1 where c1 > 3 slimit 1") # tdSql.error("select distinct c1, c2 from t1 where c1 > 3 slimit 1") # tdSql.query(f"select distinct c1, c2 from stb1 where c1 between {tbnum-2} and {tbnum} ") From 7cdedc8bc8dddfec1eb69d3ff5d6cd17571ae381 Mon Sep 17 00:00:00 2001 From: Xiaoyu Wang Date: Sun, 1 May 2022 10:24:48 +0800 Subject: [PATCH 6/6] feat: sql commadn 'select max(c1), c2 from t' --- tests/system-test/2-query/distinct.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/system-test/2-query/distinct.py b/tests/system-test/2-query/distinct.py index 8f27fe4db6..937ff78c71 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") @@ -109,9 +109,9 @@ class TDTestCase: # tdSql.error("select distinct c1, c2 from stb1 where c1 > 3 interval(1d) ") tdSql.error("select distinct c1, c2 from t1 where c1 > 3 interval(1d) ") # tdSql.error("select distinct c1, c2 from stb1 where c1 > 3 interval(1d) fill(next)") - # tdSql.error("select distinct c1, c2 from t1 where c1 > 3 interval(1d) fill(next)") + tdSql.error("select distinct c1, c2 from t1 where c1 > 3 interval(1d) fill(next)") # tdSql.error("select distinct c1, c2 from stb1 where ts > now-10d and ts < now interval(1d) fill(next)") - # tdSql.error("select distinct c1, c2 from t1 where ts > now-10d and ts < now interval(1d) fill(next)") + tdSql.error("select distinct c1, c2 from t1 where ts > now-10d and ts < now interval(1d) fill(next)") # tdSql.error("select distinct c1, c2 from stb1 where c1 > 3 slimit 1") # tdSql.error("select distinct c1, c2 from t1 where c1 > 3 slimit 1") # tdSql.query(f"select distinct c1, c2 from stb1 where c1 between {tbnum-2} and {tbnum} ")