diff --git a/include/util/tdef.h b/include/util/tdef.h index 1569e9eee3..394af567c9 100644 --- a/include/util/tdef.h +++ b/include/util/tdef.h @@ -272,7 +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_NAME_EXLEN 8 #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/libs/parser/src/parTranslater.c b/source/libs/parser/src/parTranslater.c index 6da6d84682..83ea444912 100755 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -4111,7 +4111,7 @@ static int32_t checkWinJoinAggColCoexist(STranslateContext* pCxt, SSelectStmt* p } static int32_t checkHavingGroupBy(STranslateContext* pCxt, SSelectStmt* pSelect) { - int32_t code = TSDB_CODE_SUCCESS; + int32_t code = TSDB_CODE_SUCCESS; if (NULL == getGroupByList(pCxt) && NULL == pSelect->pPartitionByList && NULL == pSelect->pWindow && !isWindowJoinStmt(pSelect)) { return code; @@ -5473,22 +5473,26 @@ static int32_t translateClausePosition(STranslateContext* pCxt, SNodeList* pProj } static int32_t rewriteColsFunction(STranslateContext* pCxt, SNodeList** nodeList, SNodeList** selectFuncList); +static int32_t rewriteHavingColsNode(STranslateContext* pCxt, SNode** pNode, SNodeList** selectFuncList); static int32_t prepareColumnExpansion(STranslateContext* pCxt, ESqlClause clause, SSelectStmt* pSelect) { int32_t code = TSDB_CODE_SUCCESS; + int32_t len = LIST_LENGTH(pSelect->pProjectionBindList); if (clause == SQL_CLAUSE_SELECT) { code = rewriteColsFunction(pCxt, &pSelect->pProjectionList, &pSelect->pProjectionBindList); + } else if (clause == SQL_CLAUSE_HAVING) { + code = rewriteHavingColsNode(pCxt, &pSelect->pHaving, &pSelect->pProjectionBindList); } else if (clause == SQL_CLAUSE_ORDER_BY) { code = rewriteColsFunction(pCxt, &pSelect->pOrderByList, &pSelect->pProjectionBindList); } else { code = generateSyntaxErrMsgExt(&pCxt->msgBuf, TSDB_CODE_PAR_WRONG_VALUE_TYPE, "Invalid clause for column expansion"); } - if (TSDB_CODE_SUCCESS == code) { + if (TSDB_CODE_SUCCESS == code && LIST_LENGTH(pSelect->pProjectionBindList) > len) { code = translateExprList(pCxt, pSelect->pProjectionBindList); } if (pSelect->pProjectionBindList != NULL) { - pSelect->hasAggFuncs = true; + pSelect->hasAggFuncs = true; } return code; } @@ -5745,10 +5749,18 @@ static int32_t translateSelectList(STranslateContext* pCxt, SSelectStmt* pSelect } static int32_t translateHaving(STranslateContext* pCxt, SSelectStmt* pSelect) { + int32_t code = TSDB_CODE_SUCCESS; if (NULL == pSelect->pGroupByList && NULL == pSelect->pPartitionByList && NULL == pSelect->pWindow && !isWindowJoinStmt(pSelect) && NULL != pSelect->pHaving) { return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION); } + pCxt->currClause = SQL_CLAUSE_HAVING; + if (NULL != pSelect->pHaving) { + code = prepareColumnExpansion(pCxt, SQL_CLAUSE_HAVING, pSelect); + if (TSDB_CODE_SUCCESS != code) { + return code; + } + } if (isWindowJoinStmt(pSelect)) { if (NULL != pSelect->pHaving) { bool hasFunc = false; @@ -5758,9 +5770,7 @@ static int32_t translateHaving(STranslateContext* pCxt, SSelectStmt* pSelect) { } } } - pCxt->currClause = SQL_CLAUSE_HAVING; - int32_t code = translateExpr(pCxt, &pSelect->pHaving); - return code; + return translateExpr(pCxt, &pSelect->pHaving); } static int32_t translateGroupBy(STranslateContext* pCxt, SSelectStmt* pSelect) { @@ -7554,6 +7564,22 @@ _end: return code; } +static int32_t rewriteHavingColsNode(STranslateContext* pCxt, SNode** pNode, SNodeList** selectFuncList) { + int32_t code = TSDB_CODE_SUCCESS; + if(!pNode || *pNode == NULL) return code; + if (isMultiColsFuncNode(*pNode)) { + parserWarn("%s Invalid using multi cols func in having.", __func__); + return TSDB_CODE_PAR_INVALID_COLS_FUNCTION; + } else { + SCheckColsFuncCxt pSelectFuncCxt = {false, selectFuncList, TSDB_CODE_SUCCESS}; + nodesRewriteExpr(pNode, rewriteSingleColsFunc, &pSelectFuncCxt); + if (pSelectFuncCxt.status != TSDB_CODE_SUCCESS) { + return pSelectFuncCxt.status; + } + } + return code; +} + static int32_t rewriteColsFunction(STranslateContext* pCxt, SNodeList** nodeList, SNodeList** selectFuncList) { int32_t code = TSDB_CODE_SUCCESS; bool needRewrite = false; diff --git a/tests/system-test/2-query/cols_function.py b/tests/system-test/2-query/cols_function.py index 2d204e514b..8b318cefb1 100644 --- a/tests/system-test/2-query/cols_function.py +++ b/tests/system-test/2-query/cols_function.py @@ -1113,6 +1113,47 @@ class TDTestCase: tdSql.checkData(2, 2, "2022-09-29 15:15:03") tdSql.checkData(3, 1, "2022-09-30 15:15:04") tdSql.checkData(3, 2, "2022-09-30 15:15:03") + + def having_test(self, table_name, is_subquery): + tdLog.info("having_test") + t1 = f"from {table_name} " + tdSql.query(f'select tbname, cols(last(ts), ts) {t1} group by tbname having cols(last(ts), ts) > 1734574929000') + tdSql.checkRows(1) + tdSql.checkCols(2) + tdSql.checkData(0, 0, 'd0') + tdSql.checkData(0, 1, 1734574929014) + tdSql.query(f'select tbname, cols(last(ts), ts) {t1} group by tbname having cols(last(ts), ts) = 1734574929000') + tdSql.checkRows(1) + tdSql.checkCols(2) + tdSql.checkData(0, 0, 'd1') + tdSql.checkData(0, 1, 1734574929000) + tdSql.query(f'select tbname, cols(last(ts), ts) {t1} group by tbname having cols(last(ts), ts) < 1734574929000') + tdSql.checkRows(0) + tdSql.query(f'select tbname, cols(last(ts), ts) {t1} group by tbname having cols(last(ts), ts) != 1734574929000') + tdSql.checkRows(1) + tdSql.checkCols(2) + tdSql.checkData(0, 0, 'd0') + tdSql.checkData(0, 1, 1734574929014) + tdSql.query(f'select tbname, cols(last(ts), ts) {t1} group by tbname having cols(last(ts), ts) >= 1734574929000') + tdSql.checkRows(2) + tdSql.checkCols(2) + tdSql.checkData(0, 0, 'd1') + tdSql.checkData(0, 1, 1734574929000) + tdSql.checkData(1, 0, 'd0') + tdSql.checkData(1, 1, 1734574929014) + tdSql.query(f'select tbname, cols(last(ts), ts) {t1} group by tbname having cols(last(ts), ts) <= 1734574929000') + tdSql.checkRows(1) + tdSql.checkCols(2) + tdSql.checkData(0, 0, 'd1') + tdSql.checkData(0, 1, 1734574929000) + tdSql.query(f'select tbname, cols(last(ts), ts) {t1} group by tbname having cols(last(c0), ts) between 1734574929000 and 1734574929014') + tdSql.checkRows(2) + tdSql.checkCols(2) + tdSql.checkData(0, 0, 'd1') + tdSql.checkData(0, 1, 1734574929000) + tdSql.checkData(1, 0, 'd0') + tdSql.checkData(1, 1, 1734574929014) + def run(self): self.funcNestTest() @@ -1128,6 +1169,8 @@ class TDTestCase: self.include_null_test() self.long_column_name_test() self.test1() + self.having_test("test.meters", False) + self.having_test("(select tbname, * from test.meters)", True) def stop(self):