Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion source/libs/executor/inc/executorInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ typedef struct SIndefRowsRuntime {
SArray* pPseudoColInfo; // pseudo-column slot mapping for direct project
SSDataBlock* pTmpBlock; // reusable temp block for one segment copy
int32_t blockCapacity; // max rows per output block
SExprSupp projSupp; // projection expressions for pure raw-col/scalar window queries
} SIndefRowsRuntime;

typedef struct SIntervalAggOperatorInfo {
Expand Down Expand Up @@ -698,7 +699,8 @@ void cleanupBasicInfo(SOptrBasicInfo* pInfo);

int32_t initExprSupp(SExprSupp* pSup, SExprInfo* pExprInfo, int32_t numOfExpr, SFunctionStateStore* pStore);
void checkIndefRowsFuncs(SExprSupp* pSup);
int32_t initIndefRowsRuntime(SIndefRowsRuntime* pRuntime, SqlFunctionCtx* pCtx, int32_t numOfExprs, int32_t blockCapacity);
int32_t initIndefRowsRuntime(SIndefRowsRuntime* pRuntime, SqlFunctionCtx* pCtx, int32_t numOfExprs, int32_t blockCapacity,
SNodeList* pProjs, SFunctionStateStore* pFuncStore);
void resetIndefRowsRuntime(SIndefRowsRuntime* pRuntime, struct SOperatorInfo* pOperator);
void cleanupIndefRowsRuntime(SIndefRowsRuntime* pRuntime, struct SOperatorInfo* pOperator);
SIndefRowsWindowState* findIndefRowsWindowState(const SIndefRowsRuntime* pRuntime, uint64_t groupId, TSKEY winSKey);
Expand Down
3 changes: 2 additions & 1 deletion source/libs/executor/src/countwindowoperator.c
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,8 @@ int32_t createCountwindowOperatorInfo(SOperatorInfo* downstream, SPhysiNode* phy

pInfo->indefRowsMode = pCountWindowNode->window.indefRowsFunc;
if (pInfo->indefRowsMode) {
code = initIndefRowsRuntime(&pInfo->indefRows, pOperator->exprSupp.pCtx, num, pOperator->resultInfo.capacity);
code = initIndefRowsRuntime(&pInfo->indefRows, pOperator->exprSupp.pCtx, num, pOperator->resultInfo.capacity,
pCountWindowNode->window.pProjs, &pTaskInfo->storageAPI.functionStore);
QUERY_CHECK_CODE(code, lino, _error);
}

Expand Down
3 changes: 2 additions & 1 deletion source/libs/executor/src/eventwindowoperator.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ int32_t createEventwindowOperatorInfo(SOperatorInfo* downstream, SPhysiNode* phy

pInfo->indefRowsMode = pEventWindowNode->window.indefRowsFunc;
if (pInfo->indefRowsMode) {
code = initIndefRowsRuntime(&pInfo->indefRows, pOperator->exprSupp.pCtx, num, pOperator->resultInfo.capacity);
code = initIndefRowsRuntime(&pInfo->indefRows, pOperator->exprSupp.pCtx, num, pOperator->resultInfo.capacity,
pEventWindowNode->window.pProjs, &pTaskInfo->storageAPI.functionStore);
QUERY_CHECK_CODE(code, lino, _error);
}

Expand Down
66 changes: 52 additions & 14 deletions source/libs/executor/src/executorInt.c
Original file line number Diff line number Diff line change
Expand Up @@ -1250,17 +1250,13 @@ static int32_t createIndefRowsWindowState(SIndefRowsRuntime* pRuntime, SSDataBlo
return code;
}

static int32_t fillIndefRowsWindowPseudoCols(SOperatorInfo* pOperator, SSDataBlock* pBlock, const STimeWindow* pWin,
int32_t startOffset, int32_t rows) {
static int32_t fillPseudoColsInExprSupp(SExprSupp* pSupp, SSDataBlock* pBlock, const STimeWindow* pWin,
int32_t startOffset, int32_t rows) {
int32_t code = TSDB_CODE_SUCCESS;
int32_t lino = 0;

if (rows <= 0) {
return code;
}

for (int32_t i = 0; i < pOperator->exprSupp.numOfExprs; ++i) {
SqlFunctionCtx* pCtx = &pOperator->exprSupp.pCtx[i];
if (rows <= 0) return code;
for (int32_t i = 0; i < pSupp->numOfExprs; ++i) {
SqlFunctionCtx* pCtx = &pSupp->pCtx[i];
if (!fmIsPseudoColumnFunc(pCtx->functionId)) {
continue;
}
Expand All @@ -1277,7 +1273,7 @@ static int32_t fillIndefRowsWindowPseudoCols(SOperatorInfo* pOperator, SSDataBlo
continue;
}

int32_t slotId = pOperator->exprSupp.pExprInfo[i].base.resSchema.slotId;
int32_t slotId = pSupp->pExprInfo[i].base.resSchema.slotId;
SColumnInfoData* pCol = taosArrayGet(pBlock->pDataBlock, slotId);
QUERY_CHECK_NULL(pCol, code, lino, _return, terrno);

Expand All @@ -1294,7 +1290,20 @@ static int32_t fillIndefRowsWindowPseudoCols(SOperatorInfo* pOperator, SSDataBlo
return code;
}

int32_t initIndefRowsRuntime(SIndefRowsRuntime* pRuntime, SqlFunctionCtx* pCtx, int32_t numOfExprs, int32_t blockCapacity) {
static int32_t fillIndefRowsWindowPseudoCols(SOperatorInfo* pOperator, SIndefRowsRuntime* pRuntime,
SSDataBlock* pBlock, const STimeWindow* pWin,
int32_t startOffset, int32_t rows) {
if (rows <= 0) return TSDB_CODE_SUCCESS;
int32_t code = fillPseudoColsInExprSupp(&pOperator->exprSupp, pBlock, pWin, startOffset, rows);
if (TSDB_CODE_SUCCESS != code) return code;
if (pRuntime != NULL && pRuntime->projSupp.numOfExprs > 0) {
code = fillPseudoColsInExprSupp(&pRuntime->projSupp, pBlock, pWin, startOffset, rows);
}
return code;
}

int32_t initIndefRowsRuntime(SIndefRowsRuntime* pRuntime, SqlFunctionCtx* pCtx, int32_t numOfExprs, int32_t blockCapacity,
SNodeList* pProjs, SFunctionStateStore* pFuncStore) {
int32_t code = TSDB_CODE_SUCCESS;
int32_t lino = 0;

Expand All @@ -1313,10 +1322,21 @@ int32_t initIndefRowsRuntime(SIndefRowsRuntime* pRuntime, SqlFunctionCtx* pCtx,
code = setRowTsColumnOutputInfo(pCtx, numOfExprs, &pRuntime->pPseudoColInfo);
QUERY_CHECK_CODE(code, lino, _return);

// Initialize projection support if projection list is provided
if (pProjs != NULL && LIST_LENGTH(pProjs) > 0) {
int32_t numProjs = 0;
SExprInfo* pProjExprInfo = NULL;
code = createExprInfo(pProjs, NULL, &pProjExprInfo, &numProjs);
QUERY_CHECK_CODE(code, lino, _return);
code = initExprSupp(&pRuntime->projSupp, pProjExprInfo, numProjs, pFuncStore);
QUERY_CHECK_CODE(code, lino, _return);
}

return code;

_return:
qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
cleanupExprSupp(&pRuntime->projSupp);
pRuntime->pReadyBlocks = tdListFree(pRuntime->pReadyBlocks);
tSimpleHashCleanup(pRuntime->pOpenStatesMap);
pRuntime->pOpenStatesMap = NULL;
Expand Down Expand Up @@ -1368,6 +1388,7 @@ void cleanupIndefRowsRuntime(SIndefRowsRuntime* pRuntime, SOperatorInfo* pOperat
pRuntime->pReadyBlocks = tdListFree(pRuntime->pReadyBlocks);
taosArrayDestroy(pRuntime->pPseudoColInfo);
pRuntime->pPseudoColInfo = NULL;
cleanupExprSupp(&pRuntime->projSupp);
blockDataDestroy(pRuntime->pTmpBlock);
pRuntime->pTmpBlock = NULL;
}
Expand Down Expand Up @@ -1436,7 +1457,12 @@ int32_t applyIndefRowsFuncOnWindowState(SOperatorInfo* pOperator, SIndefRowsRunt
pWindowBlock->info.scanFlag = pInputBlock->info.scanFlag;
pWindowBlock->info.dataLoad = pInputBlock->info.dataLoad;

code = setInputDataBlock(&pOperator->exprSupp, pWindowBlock, inputTsOrder, pWindowBlock->info.scanFlag, false);
// Choose exprSupp or projSupp based on projection mode
if (pRuntime->projSupp.numOfExprs > 0) {
code = setInputDataBlock(&pRuntime->projSupp, pWindowBlock, inputTsOrder, pWindowBlock->info.scanFlag, false);
} else {
code = setInputDataBlock(&pOperator->exprSupp, pWindowBlock, inputTsOrder, pWindowBlock->info.scanFlag, false);
}
QUERY_CHECK_CODE(code, lino, _return);

pState->pCurBlock->info.id = pInputBlock->info.id;
Expand All @@ -1446,10 +1472,22 @@ int32_t applyIndefRowsFuncOnWindowState(SOperatorInfo* pOperator, SIndefRowsRunt
code = blockDataEnsureCapacity(pState->pCurBlock, pState->pCurBlock->info.rows + pWindowBlock->info.rows);
QUERY_CHECK_CODE(code, lino, _return);

code = projectApplyFunctions(pOperator->exprSupp.pExprInfo, pState->pCurBlock, pWindowBlock,
if (pRuntime->projSupp.numOfExprs > 0) {
// Projection path: evaluate scalar/column/pseudo-column expressions directly
code = projectApplyFunctionsWithSelect(
pRuntime->projSupp.pExprInfo, pState->pCurBlock, pWindowBlock,
pRuntime->projSupp.pCtx, pRuntime->projSupp.numOfExprs,
NULL, GET_STM_RTINFO(pOperator->pTaskInfo),
true, // doSelectFunc = true
false, // hasIndefRowsFunc = false
pOperator->pTaskInfo);
} else {
// Existing function execution path
code = projectApplyFunctions(pOperator->exprSupp.pExprInfo, pState->pCurBlock, pWindowBlock,
pOperator->exprSupp.pCtx, pOperator->exprSupp.numOfExprs,
pRuntime->pPseudoColInfo,
GET_STM_RTINFO(pOperator->pTaskInfo), pOperator->pTaskInfo);
}
QUERY_CHECK_CODE(code, lino, _return);

pState->pRow->nOrigRows += numRows;
Expand Down Expand Up @@ -1497,7 +1535,7 @@ int32_t closeIndefRowsWindowState(SOperatorInfo* pOperator, SIndefRowsRuntime* p
SSDataBlock* pBlock = *(SSDataBlock**)pNode->data;
taosMemoryFree(pNode);

code = fillIndefRowsWindowPseudoCols(pOperator, pBlock, &pState->win, 0, pBlock->info.rows);
code = fillIndefRowsWindowPseudoCols(pOperator, pRuntime, pBlock, &pState->win, 0, pBlock->info.rows);
if (code != TSDB_CODE_SUCCESS) {
blockDataDestroy(pBlock);
qError("%s fillPseudoCols failed since %s", __func__, tstrerror(code));
Expand Down
9 changes: 6 additions & 3 deletions source/libs/executor/src/timewindowoperator.c
Original file line number Diff line number Diff line change
Expand Up @@ -1761,7 +1761,8 @@ int32_t createIntervalOperatorInfo(SOperatorInfo* downstream, SIntervalPhysiNode

pInfo->indefRowsMode = pPhyNode->window.indefRowsFunc;
if (pInfo->indefRowsMode) {
code = initIndefRowsRuntime(&pInfo->indefRows, pOperator->exprSupp.pCtx, num, pOperator->resultInfo.capacity);
code = initIndefRowsRuntime(&pInfo->indefRows, pOperator->exprSupp.pCtx, num, pOperator->resultInfo.capacity,
pPhyNode->window.pProjs, &pTaskInfo->storageAPI.functionStore);
QUERY_CHECK_CODE(code, lino, _error);
}

Expand Down Expand Up @@ -2161,7 +2162,8 @@ int32_t createStatewindowOperatorInfo(SOperatorInfo* downstream, SStateWindowPhy

pInfo->indefRowsMode = pStateNode->window.indefRowsFunc;
if (pInfo->indefRowsMode) {
code = initIndefRowsRuntime(&pInfo->indefRows, pOperator->exprSupp.pCtx, num, pOperator->resultInfo.capacity);
code = initIndefRowsRuntime(&pInfo->indefRows, pOperator->exprSupp.pCtx, num, pOperator->resultInfo.capacity,
pStateNode->window.pProjs, &pTaskInfo->storageAPI.functionStore);
QUERY_CHECK_CODE(code, lino, _error);
}

Expand Down Expand Up @@ -2296,7 +2298,8 @@ int32_t createSessionAggOperatorInfo(SOperatorInfo* downstream, SSessionWinodwPh

pInfo->indefRowsMode = pSessionNode->window.indefRowsFunc;
if (pInfo->indefRowsMode) {
code = initIndefRowsRuntime(&pInfo->indefRows, pOperator->exprSupp.pCtx, numOfCols, pOperator->resultInfo.capacity);
code = initIndefRowsRuntime(&pInfo->indefRows, pOperator->exprSupp.pCtx, numOfCols, pOperator->resultInfo.capacity,
pSessionNode->window.pProjs, &pTaskInfo->storageAPI.functionStore);
QUERY_CHECK_CODE(code, lino, _error);
}

Expand Down
59 changes: 57 additions & 2 deletions source/libs/parser/src/parTranslater.c
Original file line number Diff line number Diff line change
Expand Up @@ -5365,6 +5365,14 @@ static EDealRes doCheckExprForGroupBy(SNode** pNode, void* pContext) {
}
if (pSelect->pWindow && isSingleTable &&
((QUERY_NODE_COLUMN == nodeType(*pNode) && ((SColumnNode*)*pNode)->colType == COLUMN_TYPE_TAG))) {
// In projection mode, HAVING/ORDER BY tags must use _group_key (not _select_value)
// because _select_value requires companion agg functions that don't exist in projection mode.
bool isProjMode = (nodeType(pSelect->pWindow) != QUERY_NODE_EXTERNAL_WINDOW &&
!pSelect->hasAggFuncs &&
!(pSelect->hasIndefiniteRowsFunc && pSelect->hasSelectFunc));
if (isProjMode && (pCxt->currClause == SQL_CLAUSE_HAVING || pCxt->currClause == SQL_CLAUSE_ORDER_BY)) {
return rewriteExprToGroupKeyFunc(pCxt, pNode);
}
return rewriteExprToSelectTagFunc(pCxt, pNode);
}
if (pSelect->pWindow && isSingleTable && isTbnameFuction(*pNode)) {
Expand All @@ -5373,17 +5381,40 @@ static EDealRes doCheckExprForGroupBy(SNode** pNode, void* pContext) {

SNode* pPartKey = NULL;
bool partionByTbname = hasTbnameFunction(pSelect->pPartitionByList);

// Projection mode: window query with no agg functions and no real select/indef-rows functions.
// During SELECT processing: hasIndefiniteRowsFunc is not yet set (false).
// During HAVING processing: hasIndefiniteRowsFunc is artificially true but hasSelectFunc is still false.
// In both cases, skip rewriting — columns stay as-is for the projection list path.
// For csum mode (hasIndefiniteRowsFunc=true AND hasSelectFunc=true), this does NOT fire.
// Exception: TAG columns in HAVING/ORDER BY must fall through to the partition-by loop for
// _group_key rewrite, so the planner can create output slots for tags not in SELECT.
if (NULL != pSelect->pWindow &&
nodeType(pSelect->pWindow) != QUERY_NODE_EXTERNAL_WINDOW &&
!pSelect->hasAggFuncs &&
!(pSelect->hasIndefiniteRowsFunc && pSelect->hasSelectFunc) &&
(QUERY_NODE_COLUMN == nodeType(*pNode) || isScanPseudoColumnFunc(*pNode))) {
bool isClauseTag = ((pCxt->currClause == SQL_CLAUSE_HAVING || pCxt->currClause == SQL_CLAUSE_ORDER_BY) &&
QUERY_NODE_COLUMN == nodeType(*pNode) &&
((SColumnNode*)*pNode)->colType == COLUMN_TYPE_TAG);
if (!isClauseTag) {
return DEAL_RES_CONTINUE;
}
}

FOREACH(pPartKey, pSelect->pPartitionByList) {
if (nodesEqualNode(pPartKey, *pNode)) {
return (pSelect->hasAggFuncs || pSelect->pWindow) ? rewriteExprToGroupKeyFunc(pCxt, pNode)
: DEAL_RES_IGNORE_CHILD;
}
if ((partionByTbname) && QUERY_NODE_COLUMN == nodeType(*pNode) &&
((SColumnNode*)*pNode)->colType == COLUMN_TYPE_TAG) {
return rewriteExprToGroupKeyFunc(pCxt, pNode);
return (pSelect->hasAggFuncs || pSelect->pWindow) ? rewriteExprToGroupKeyFunc(pCxt, pNode)
: DEAL_RES_IGNORE_CHILD;
}
if (IsEqualTbNameFuncNode(pSelect, pPartKey, *pNode)) {
return rewriteExprToGroupKeyFunc(pCxt, pNode);
return (pSelect->hasAggFuncs || pSelect->pWindow) ? rewriteExprToGroupKeyFunc(pCxt, pNode)
: DEAL_RES_IGNORE_CHILD;
}
}
if (NULL != pSelect->pWindow && QUERY_NODE_STATE_WINDOW == nodeType(pSelect->pWindow)) {
Expand Down Expand Up @@ -11701,6 +11732,30 @@ static int32_t translateSelectFrom(STranslateContext* pCxt, SSelectStmt* pSelect
if (TSDB_CODE_SUCCESS == code) {
code = translateSelectList(pCxt, pSelect);
}
if (TSDB_CODE_SUCCESS == code &&
NULL != pSelect->pWindow &&
nodeType(pSelect->pWindow) != QUERY_NODE_EXTERNAL_WINDOW &&
!pSelect->hasAggFuncs &&
!pSelect->hasIndefiniteRowsFunc &&
!pSelect->hasInterpFunc &&
!pSelect->hasForecastFunc) {
pSelect->hasIndefiniteRowsFunc = true;
if (QUERY_NODE_INTERVAL_WINDOW == nodeType(pSelect->pWindow)) {
SIntervalWindowNode* pInterval = (SIntervalWindowNode*)pSelect->pWindow;
if (NULL != pInterval->pFill) {
SFillNode* pFillNode = (SFillNode*)pInterval->pFill;
if (pFillNode->mode != FILL_MODE_NONE &&
pFillNode->mode != FILL_MODE_NULL &&
pFillNode->mode != FILL_MODE_NULL_F &&
pFillNode->mode != FILL_MODE_VALUE &&
pFillNode->mode != FILL_MODE_VALUE_F) {
code = generateSyntaxErrMsgExt(&pCxt->msgBuf, TSDB_CODE_PAR_FILL_NOT_ALLOWED_FUNC,
"Only FILL(NONE/NULL/NULL_F/VALUE/VALUE_F) is supported "
"when SELECT list contains no aggregate functions");
}
}
}
}
if (TSDB_CODE_SUCCESS == code) {
code = checkHavingGroupBy(pCxt, pSelect);
}
Expand Down
101 changes: 99 additions & 2 deletions source/libs/planner/src/planLogicCreater.c
Original file line number Diff line number Diff line change
Expand Up @@ -1900,9 +1900,106 @@ static int32_t createWindowLogicNodeFinalize(SLogicPlanContext* pCxt, SSelectStm
pSelect->hasIndefiniteRowsFunc ? fmIsWindowIndefRowsFunc : fmIsWindowClauseFunc,
&pWindow->pFuncs));

PLAN_ERR_JRET(rewriteExprsForSelect(pWindow->pFuncs, pSelect, SQL_CLAUSE_WINDOW, NULL));
// Detect projection-only mode: pFuncs is NULL or contains only pseudo-column and _group_key functions
// (_group_key comes from HAVING tag columns that need slots for condition evaluation)
bool projectionMode = true;
if (pWindow->pFuncs != NULL) {
SNode* pNode = NULL;
FOREACH(pNode, pWindow->pFuncs) {
if (nodeType(pNode) == QUERY_NODE_FUNCTION) {
SFunctionNode* pFunc = (SFunctionNode*)pNode;
if (!fmIsPseudoColumnFunc(pFunc->funcId) && !fmIsGroupKeyFunc(pFunc->funcId)) {
projectionMode = false;
break;
}
}
}
}

if (projectionMode && pSelect->hasIndefiniteRowsFunc) {
// When used as subquery, the outer query may prune pProjectionList to contain only
// unnamed VALUE placeholders (e.g., SELECT count(*) FROM (subquery)). In that case,
// use the child node's first target (primary key) as a minimal projection to ensure
// the window operator still produces output rows.
bool hasNamedExpr = false;
SNode* pTmp = NULL;
FOREACH(pTmp, pSelect->pProjectionList) {
if (nodeType(pTmp) == QUERY_NODE_COLUMN || ((SExprNode*)pTmp)->aliasName[0] != '\0') {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The check for hasNamedExpr is too restrictive. It currently only considers raw columns or aliased expressions as "named". This will cause unaliased scalar functions (e.g., SELECT sin(current) FROM ...) or scalar expressions (e.g., SELECT current + 1 FROM ...) to be incorrectly treated as degenerate placeholders, leading to them being replaced by a minimal projection (primary key) and causing subsequent evaluation failures in parent nodes.

      if (nodeType(pTmp) == QUERY_NODE_COLUMN || nodeType(pTmp) == QUERY_NODE_FUNCTION || nodeType(pTmp) == QUERY_NODE_EXPRESSION || ((SExprNode*)pTmp)->aliasName[0] != '\0') {

hasNamedExpr = true;
break;
}
}

if (hasNamedExpr) {
PLAN_ERR_JRET(nodesCloneList(pSelect->pProjectionList, &pWindow->pProjs));
} else {
// pProjectionList is degenerate (pruned by outer query) — use child's first target
// as minimal projection to ensure window produces output rows
SNode* pFirstTarget = nodesListGetNode(pCxt->pCurrRoot->pTargets, 0);
if (pFirstTarget != NULL) {
SNode* pClone = NULL;
PLAN_ERR_JRET(nodesCloneNode(pFirstTarget, &pClone));
PLAN_ERR_JRET(nodesListMakeStrictAppend(&pWindow->pProjs, pClone));
Comment on lines +1941 to +1942
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Potential memory leak: if nodesListMakeStrictAppend fails, the cloned node pClone is not destroyed before returning.

        PLAN_ERR_JRET(nodesCloneNode(pFirstTarget, &pClone));
        int32_t code = nodesListMakeStrictAppend(&pWindow->pProjs, pClone);
        if (code != TSDB_CODE_SUCCESS) {
          nodesDestroyNode(pClone);
          return code;
        }

}
}

// Merge remaining pFuncs entries (pseudo columns + _group_key) into pProjs.
// These come from HAVING/ORDER BY expressions that reference tags or pseudo columns
// not in SELECT — the physi creator needs output slots for them.
if (pWindow->pFuncs != NULL) {
SNode* pFuncNode = NULL;
FOREACH(pFuncNode, pWindow->pFuncs) {
bool alreadyInProjs = false;
SNode* pProjNode = NULL;
FOREACH(pProjNode, pWindow->pProjs) {
if (nodesEqualNode(pProjNode, pFuncNode)) {
alreadyInProjs = true;
break;
}
}
if (!alreadyInProjs) {
SNode* pClone = NULL;
PLAN_ERR_JRET(nodesCloneNode(pFuncNode, &pClone));
PLAN_ERR_JRET(nodesListMakeStrictAppend(&pWindow->pProjs, pClone));
Comment on lines +1962 to +1963
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Potential memory leak: if nodesListMakeStrictAppend fails, the cloned node pClone is not destroyed before returning.

          PLAN_ERR_JRET(nodesCloneNode(pFuncNode, &pClone));
          int32_t code = nodesListMakeStrictAppend(&pWindow->pProjs, pClone);
          if (code != TSDB_CODE_SUCCESS) {
            nodesDestroyNode(pClone);
            return code;
          }

}
}
}

PLAN_ERR_JRET(createColumnByRewriteExprs(pWindow->pFuncs, &pWindow->node.pTargets));
// Collect column references from HAVING that may not be in pProjs
// (e.g., HAVING col NOT in SELECT, or SELECT pruned by outer subquery).
if (pSelect->pHaving != NULL) {
SNodeList* pHavingCols = NULL;
PLAN_ERR_JRET(nodesCollectColumnsFromNode(pSelect->pHaving, NULL, COLLECT_COL_TYPE_ALL, &pHavingCols));
SNode* pColNode = NULL;
FOREACH(pColNode, pHavingCols) {
bool found = false;
SNode* pProjNode = NULL;
FOREACH(pProjNode, pWindow->pProjs) {
if (nodesEqualNode(pProjNode, pColNode)) {
found = true;
break;
}
}
if (!found) {
SNode* pClone = NULL;
PLAN_ERR_JRET(nodesCloneNode(pColNode, &pClone));
PLAN_ERR_JRET(nodesListMakeStrictAppend(&pWindow->pProjs, pClone));
Comment on lines +1985 to +1986
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Potential memory leak: if nodesListMakeStrictAppend fails, the cloned node pClone is not destroyed before returning.

          PLAN_ERR_JRET(nodesCloneNode(pColNode, &pClone));
          int32_t code = nodesListMakeStrictAppend(&pWindow->pProjs, pClone);
          if (code != TSDB_CODE_SUCCESS) {
            nodesDestroyNode(pClone);
            nodesDestroyList(pHavingCols);
            return code;
          }

}
}
nodesDestroyList(pHavingCols);
Comment on lines +1972 to +1989
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Potential memory leak: pHavingCols is allocated via nodesCollectColumnsFromNode but will be leaked if any PLAN_ERR_JRET (or other early return) occurs before nodesDestroyList(pHavingCols) is reached at line 1989.

}
Comment on lines +1970 to +1990

// Discard pFuncs (pseudo-cols and _group_key are now in pProjs)
nodesDestroyList(pWindow->pFuncs);
pWindow->pFuncs = NULL;

PLAN_ERR_JRET(rewriteExprsForSelect(pWindow->pProjs, pSelect, SQL_CLAUSE_WINDOW, NULL));
PLAN_ERR_JRET(createColumnByRewriteExprs(pWindow->pProjs, &pWindow->node.pTargets));
} else {
// Existing function-collection path
PLAN_ERR_JRET(rewriteExprsForSelect(pWindow->pFuncs, pSelect, SQL_CLAUSE_WINDOW, NULL));
PLAN_ERR_JRET(createColumnByRewriteExprs(pWindow->pFuncs, &pWindow->node.pTargets));
}

if (NULL != pSelect->pHaving && !havingHandledByFill) {
PLAN_ERR_JRET(nodesCloneNode(pSelect->pHaving, &pWindow->node.pConditions));
Expand Down
Loading
Loading