/* * Copyright (c) 2019 TAOS Data, Inc. * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "filter.h" #include "functionMgt.h" #include "planInt.h" #include "tglobal.h" #include "ttime.h" #define OPTIMIZE_FLAG_MASK(n) (1 << n) #define OPTIMIZE_FLAG_SCAN_PATH OPTIMIZE_FLAG_MASK(0) #define OPTIMIZE_FLAG_PUSH_DOWN_CONDE OPTIMIZE_FLAG_MASK(1) #define OPTIMIZE_FLAG_SET_MASK(val, mask) (val) |= (mask) #define OPTIMIZE_FLAG_TEST_MASK(val, mask) (((val) & (mask)) != 0) typedef struct SOptimizeContext { SPlanContext* pPlanCxt; bool optimized; } SOptimizeContext; typedef int32_t (*FOptimize)(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan); typedef struct SOptimizeRule { char* pName; FOptimize optimizeFunc; } SOptimizeRule; typedef enum EScanOrder { SCAN_ORDER_ASC = 1, SCAN_ORDER_DESC, SCAN_ORDER_BOTH } EScanOrder; typedef struct SOsdInfo { SScanLogicNode* pScan; SNodeList* pSdrFuncs; SNodeList* pDsoFuncs; EScanOrder scanOrder; } SOsdInfo; typedef struct SCpdIsMultiTableCondCxt { SNodeList* pLeftCols; SNodeList* pRightCols; bool havaLeftCol; bool haveRightCol; } SCpdIsMultiTableCondCxt; typedef enum ECondAction { COND_ACTION_STAY = 1, COND_ACTION_PUSH_JOIN, COND_ACTION_PUSH_LEFT_CHILD, COND_ACTION_PUSH_RIGHT_CHILD // after supporting outer join, there are other possibilities } ECondAction; typedef bool (*FMayBeOptimized)(SLogicNode* pNode); static SLogicNode* optFindPossibleNode(SLogicNode* pNode, FMayBeOptimized func) { if (func(pNode)) { return pNode; } SNode* pChild; FOREACH(pChild, pNode->pChildren) { SLogicNode* pScanNode = optFindPossibleNode((SLogicNode*)pChild, func); if (NULL != pScanNode) { return pScanNode; } } return NULL; } static void optResetParent(SLogicNode* pNode) { SNode* pChild = NULL; FOREACH(pChild, pNode->pChildren) { ((SLogicNode*)pChild)->pParent = pNode; } } static EDealRes optRebuildTbanme(SNode** pNode, void* pContext) { if (QUERY_NODE_COLUMN == nodeType(*pNode) && COLUMN_TYPE_TBNAME == ((SColumnNode*)*pNode)->colType) { SFunctionNode* pFunc = (SFunctionNode*)nodesMakeNode(QUERY_NODE_FUNCTION); if (NULL == pFunc) { *(int32_t*)pContext = TSDB_CODE_OUT_OF_MEMORY; return DEAL_RES_ERROR; } strcpy(pFunc->functionName, "tbname"); pFunc->funcType = FUNCTION_TYPE_TBNAME; pFunc->node.resType = ((SColumnNode*)*pNode)->node.resType; nodesDestroyNode(*pNode); *pNode = (SNode*)pFunc; return DEAL_RES_IGNORE_CHILD; } return DEAL_RES_CONTINUE; } static void optSetParentOrder(SLogicNode* pNode, EOrder order) { if (NULL == pNode) { return; } switch (nodeType(pNode)) { case QUERY_NODE_LOGIC_PLAN_WINDOW: ((SWindowLogicNode*)pNode)->inputTsOrder = order; // window has a sorting function, and the operator behind it uses its output order return; case QUERY_NODE_LOGIC_PLAN_JOIN: ((SJoinLogicNode*)pNode)->inputTsOrder = order; break; case QUERY_NODE_LOGIC_PLAN_FILL: ((SFillLogicNode*)pNode)->inputTsOrder = order; break; default: break; } optSetParentOrder(pNode->pParent, order); } EDealRes scanPathOptHaveNormalColImpl(SNode* pNode, void* pContext) { if (QUERY_NODE_COLUMN == nodeType(pNode)) { *((bool*)pContext) = (COLUMN_TYPE_TAG != ((SColumnNode*)pNode)->colType && COLUMN_TYPE_TBNAME != ((SColumnNode*)pNode)->colType); return *((bool*)pContext) ? DEAL_RES_END : DEAL_RES_IGNORE_CHILD; } return DEAL_RES_CONTINUE; } static bool scanPathOptHaveNormalCol(SNodeList* pList) { bool res = false; nodesWalkExprsPostOrder(pList, scanPathOptHaveNormalColImpl, &res); return res; } static bool scanPathOptMayBeOptimized(SLogicNode* pNode) { if (OPTIMIZE_FLAG_TEST_MASK(pNode->optimizedFlag, OPTIMIZE_FLAG_SCAN_PATH)) { return false; } if (QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(pNode)) { return false; } if (NULL == pNode->pParent || (QUERY_NODE_LOGIC_PLAN_WINDOW != nodeType(pNode->pParent) && QUERY_NODE_LOGIC_PLAN_AGG != nodeType(pNode->pParent) && QUERY_NODE_LOGIC_PLAN_PARTITION != nodeType(pNode->pParent))) { return false; } if ((QUERY_NODE_LOGIC_PLAN_WINDOW == nodeType(pNode->pParent) && WINDOW_TYPE_INTERVAL == ((SWindowLogicNode*)pNode->pParent)->winType) || (QUERY_NODE_LOGIC_PLAN_PARTITION == nodeType(pNode->pParent) && pNode->pParent->pParent && QUERY_NODE_LOGIC_PLAN_WINDOW == nodeType(pNode->pParent->pParent) && WINDOW_TYPE_INTERVAL == ((SWindowLogicNode*)pNode->pParent)->winType)) { return true; } if (QUERY_NODE_LOGIC_PLAN_AGG == nodeType(pNode->pParent)) { return !scanPathOptHaveNormalCol(((SAggLogicNode*)pNode->pParent)->pGroupKeys); } return false; } static SNodeList* scanPathOptGetAllFuncs(SLogicNode* pNode) { switch (nodeType(pNode)) { case QUERY_NODE_LOGIC_PLAN_WINDOW: return ((SWindowLogicNode*)pNode)->pFuncs; case QUERY_NODE_LOGIC_PLAN_AGG: return ((SAggLogicNode*)pNode)->pAggFuncs; default: break; } return NULL; } static bool scanPathOptNeedOptimizeDataRequire(const SFunctionNode* pFunc) { if (!fmIsSpecialDataRequiredFunc(pFunc->funcId)) { return false; } SNode* pPara = NULL; FOREACH(pPara, pFunc->pParameterList) { if (QUERY_NODE_COLUMN != nodeType(pPara) && QUERY_NODE_VALUE != nodeType(pPara)) { return false; } } return true; } static bool scanPathOptNeedDynOptimize(const SFunctionNode* pFunc) { if (!fmIsDynamicScanOptimizedFunc(pFunc->funcId)) { return false; } SNode* pPara = NULL; FOREACH(pPara, pFunc->pParameterList) { if (QUERY_NODE_COLUMN != nodeType(pPara) && QUERY_NODE_VALUE != nodeType(pPara)) { return false; } } return true; } static int32_t scanPathOptGetRelatedFuncs(SScanLogicNode* pScan, SNodeList** pSdrFuncs, SNodeList** pDsoFuncs) { SNodeList* pAllFuncs = scanPathOptGetAllFuncs(pScan->node.pParent); SNodeList* pTmpSdrFuncs = NULL; SNodeList* pTmpDsoFuncs = NULL; SNode* pNode = NULL; bool otherFunc = false; FOREACH(pNode, pAllFuncs) { SFunctionNode* pFunc = (SFunctionNode*)pNode; int32_t code = TSDB_CODE_SUCCESS; if (scanPathOptNeedOptimizeDataRequire(pFunc)) { code = nodesListMakeStrictAppend(&pTmpSdrFuncs, nodesCloneNode(pNode)); } else if (scanPathOptNeedDynOptimize(pFunc)) { code = nodesListMakeStrictAppend(&pTmpDsoFuncs, nodesCloneNode(pNode)); } else { otherFunc = true; break; } if (TSDB_CODE_SUCCESS != code) { nodesDestroyList(pTmpSdrFuncs); nodesDestroyList(pTmpDsoFuncs); return code; } } if (otherFunc) { nodesDestroyList(pTmpSdrFuncs); nodesDestroyList(pTmpDsoFuncs); } else { *pSdrFuncs = pTmpSdrFuncs; *pDsoFuncs = pTmpDsoFuncs; } return TSDB_CODE_SUCCESS; } static int32_t scanPathOptGetScanOrder(SScanLogicNode* pScan, EScanOrder* pScanOrder) { SNodeList* pAllFuncs = scanPathOptGetAllFuncs(pScan->node.pParent); SNode* pNode = NULL; bool hasFirst = false; bool hasLast = false; bool otherFunc = false; FOREACH(pNode, pAllFuncs) { SFunctionNode* pFunc = (SFunctionNode*)pNode; if (FUNCTION_TYPE_FIRST == pFunc->funcType) { hasFirst = true; } else if (FUNCTION_TYPE_LAST == pFunc->funcType || FUNCTION_TYPE_LAST_ROW == pFunc->funcType) { hasLast = true; } else if (FUNCTION_TYPE_SELECT_VALUE != pFunc->funcType) { otherFunc = true; } } if (hasFirst && hasLast && !otherFunc) { *pScanOrder = SCAN_ORDER_BOTH; } else if (hasLast) { *pScanOrder = SCAN_ORDER_DESC; } else { *pScanOrder = SCAN_ORDER_ASC; } return TSDB_CODE_SUCCESS; } static int32_t scanPathOptSetOsdInfo(SOsdInfo* pInfo) { int32_t code = scanPathOptGetRelatedFuncs(pInfo->pScan, &pInfo->pSdrFuncs, &pInfo->pDsoFuncs); if (TSDB_CODE_SUCCESS == code) { code = scanPathOptGetScanOrder(pInfo->pScan, &pInfo->scanOrder); } return code; } static int32_t scanPathOptMatch(SOptimizeContext* pCxt, SLogicNode* pLogicNode, SOsdInfo* pInfo) { pInfo->pScan = (SScanLogicNode*)optFindPossibleNode(pLogicNode, scanPathOptMayBeOptimized); if (NULL == pInfo->pScan) { return TSDB_CODE_SUCCESS; } return scanPathOptSetOsdInfo(pInfo); } static EFuncDataRequired scanPathOptPromoteDataRequired(EFuncDataRequired l, EFuncDataRequired r) { switch (l) { case FUNC_DATA_REQUIRED_DATA_LOAD: return l; case FUNC_DATA_REQUIRED_SMA_LOAD: return FUNC_DATA_REQUIRED_DATA_LOAD == r ? r : l; case FUNC_DATA_REQUIRED_NOT_LOAD: return FUNC_DATA_REQUIRED_FILTEROUT == r ? l : r; default: break; } return r; } static int32_t scanPathOptGetDataRequired(SNodeList* pFuncs) { if (NULL == pFuncs) { return FUNC_DATA_REQUIRED_DATA_LOAD; } EFuncDataRequired dataRequired = FUNC_DATA_REQUIRED_FILTEROUT; SNode* pFunc = NULL; FOREACH(pFunc, pFuncs) { dataRequired = scanPathOptPromoteDataRequired(dataRequired, fmFuncDataRequired((SFunctionNode*)pFunc, NULL)); } return dataRequired; } static void scanPathOptSetScanWin(SScanLogicNode* pScan) { SLogicNode* pParent = pScan->node.pParent; if (QUERY_NODE_LOGIC_PLAN_PARTITION == nodeType(pParent) && pParent->pParent && QUERY_NODE_LOGIC_PLAN_WINDOW == nodeType(pParent->pParent)) { pParent = pParent->pParent; } if (QUERY_NODE_LOGIC_PLAN_WINDOW == nodeType(pParent)) { pScan->interval = ((SWindowLogicNode*)pParent)->interval; pScan->offset = ((SWindowLogicNode*)pParent)->offset; pScan->sliding = ((SWindowLogicNode*)pParent)->sliding; pScan->intervalUnit = ((SWindowLogicNode*)pParent)->intervalUnit; pScan->slidingUnit = ((SWindowLogicNode*)pParent)->slidingUnit; pScan->triggerType = ((SWindowLogicNode*)pParent)->triggerType; pScan->watermark = ((SWindowLogicNode*)pParent)->watermark; pScan->igExpired = ((SWindowLogicNode*)pParent)->igExpired; } } static void scanPathOptSetScanOrder(EScanOrder scanOrder, SScanLogicNode* pScan) { if (pScan->sortPrimaryKey || pScan->scanSeq[0] > 1 || pScan->scanSeq[1] > 1) { return; } switch (scanOrder) { case SCAN_ORDER_ASC: pScan->scanSeq[0] = 1; pScan->scanSeq[1] = 0; optSetParentOrder(pScan->node.pParent, ORDER_ASC); break; case SCAN_ORDER_DESC: pScan->scanSeq[0] = 0; pScan->scanSeq[1] = 1; optSetParentOrder(pScan->node.pParent, ORDER_DESC); break; case SCAN_ORDER_BOTH: pScan->scanSeq[0] = 1; pScan->scanSeq[1] = 1; break; default: break; } } static int32_t scanPathOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) { SOsdInfo info = {.scanOrder = SCAN_ORDER_ASC}; int32_t code = scanPathOptMatch(pCxt, pLogicSubplan->pNode, &info); if (TSDB_CODE_SUCCESS == code && info.pScan) { scanPathOptSetScanWin(info.pScan); scanPathOptSetScanOrder(info.scanOrder, info.pScan); } if (TSDB_CODE_SUCCESS == code && (NULL != info.pDsoFuncs || NULL != info.pSdrFuncs)) { info.pScan->dataRequired = scanPathOptGetDataRequired(info.pSdrFuncs); info.pScan->pDynamicScanFuncs = info.pDsoFuncs; } if (TSDB_CODE_SUCCESS == code && info.pScan) { OPTIMIZE_FLAG_SET_MASK(info.pScan->node.optimizedFlag, OPTIMIZE_FLAG_SCAN_PATH); pCxt->optimized = true; } nodesDestroyList(info.pSdrFuncs); return code; } static int32_t pushDownCondOptMergeCond(SNode** pDst, SNode** pSrc) { SLogicConditionNode* pLogicCond = (SLogicConditionNode*)nodesMakeNode(QUERY_NODE_LOGIC_CONDITION); if (NULL == pLogicCond) { return TSDB_CODE_OUT_OF_MEMORY; } pLogicCond->node.resType.type = TSDB_DATA_TYPE_BOOL; pLogicCond->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_BOOL].bytes; pLogicCond->condType = LOGIC_COND_TYPE_AND; int32_t code = nodesListMakeAppend(&pLogicCond->pParameterList, *pSrc); if (TSDB_CODE_SUCCESS == code) { *pSrc = NULL; code = nodesListMakeAppend(&pLogicCond->pParameterList, *pDst); } if (TSDB_CODE_SUCCESS == code) { *pDst = (SNode*)pLogicCond; } else { nodesDestroyNode((SNode*)pLogicCond); } return code; } static int32_t pushDownCondOptAppendCond(SNode** pCond, SNode** pAdditionalCond) { if (NULL == *pCond) { TSWAP(*pCond, *pAdditionalCond); return TSDB_CODE_SUCCESS; } int32_t code = TSDB_CODE_SUCCESS; if (QUERY_NODE_LOGIC_CONDITION == nodeType(*pCond) && LOGIC_COND_TYPE_AND == ((SLogicConditionNode*)*pCond)->condType) { code = nodesListAppend(((SLogicConditionNode*)*pCond)->pParameterList, *pAdditionalCond); if (TSDB_CODE_SUCCESS == code) { *pAdditionalCond = NULL; } } else { code = pushDownCondOptMergeCond(pCond, pAdditionalCond); } return code; } static int32_t pushDownCondOptCalcTimeRange(SOptimizeContext* pCxt, SScanLogicNode* pScan, SNode** pPrimaryKeyCond, SNode** pOtherCond) { int32_t code = TSDB_CODE_SUCCESS; if (pCxt->pPlanCxt->topicQuery || pCxt->pPlanCxt->streamQuery) { code = pushDownCondOptAppendCond(pOtherCond, pPrimaryKeyCond); } else { bool isStrict = false; code = filterGetTimeRange(*pPrimaryKeyCond, &pScan->scanRange, &isStrict); if (TSDB_CODE_SUCCESS == code) { if (isStrict) { nodesDestroyNode(*pPrimaryKeyCond); } else { code = pushDownCondOptAppendCond(pOtherCond, pPrimaryKeyCond); } *pPrimaryKeyCond = NULL; } } return code; } static int32_t pushDownCondOptRebuildTbanme(SNode** pTagCond) { int32_t code = TSDB_CODE_SUCCESS; nodesRewriteExpr(pTagCond, optRebuildTbanme, &code); return code; } static int32_t pushDownCondOptDealScan(SOptimizeContext* pCxt, SScanLogicNode* pScan) { if (NULL == pScan->node.pConditions || OPTIMIZE_FLAG_TEST_MASK(pScan->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE) || TSDB_SYSTEM_TABLE == pScan->tableType) { return TSDB_CODE_SUCCESS; } SNode* pPrimaryKeyCond = NULL; SNode* pOtherCond = NULL; int32_t code = filterPartitionCond(&pScan->node.pConditions, &pPrimaryKeyCond, &pScan->pTagIndexCond, &pScan->pTagCond, &pOtherCond); if (TSDB_CODE_SUCCESS == code && NULL != pScan->pTagCond) { code = pushDownCondOptRebuildTbanme(&pScan->pTagCond); } if (TSDB_CODE_SUCCESS == code && NULL != pPrimaryKeyCond) { code = pushDownCondOptCalcTimeRange(pCxt, pScan, &pPrimaryKeyCond, &pOtherCond); } if (TSDB_CODE_SUCCESS == code) { pScan->node.pConditions = pOtherCond; } if (TSDB_CODE_SUCCESS == code) { OPTIMIZE_FLAG_SET_MASK(pScan->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE); pCxt->optimized = true; } else { nodesDestroyNode(pPrimaryKeyCond); nodesDestroyNode(pOtherCond); } return code; } static bool pushDownCondOptBelongThisTable(SNode* pCondCol, SNodeList* pTableCols) { SNode* pTableCol = NULL; FOREACH(pTableCol, pTableCols) { if (nodesEqualNode(pCondCol, pTableCol)) { return true; } } return false; } static EDealRes pushDownCondOptIsCrossTableCond(SNode* pNode, void* pContext) { SCpdIsMultiTableCondCxt* pCxt = pContext; if (QUERY_NODE_COLUMN == nodeType(pNode)) { if (pushDownCondOptBelongThisTable(pNode, pCxt->pLeftCols)) { pCxt->havaLeftCol = true; } else if (pushDownCondOptBelongThisTable(pNode, pCxt->pRightCols)) { pCxt->haveRightCol = true; } return pCxt->havaLeftCol && pCxt->haveRightCol ? DEAL_RES_END : DEAL_RES_CONTINUE; } return DEAL_RES_CONTINUE; } static ECondAction pushDownCondOptGetCondAction(EJoinType joinType, SNodeList* pLeftCols, SNodeList* pRightCols, SNode* pNode) { SCpdIsMultiTableCondCxt cxt = { .pLeftCols = pLeftCols, .pRightCols = pRightCols, .havaLeftCol = false, .haveRightCol = false}; nodesWalkExpr(pNode, pushDownCondOptIsCrossTableCond, &cxt); return (JOIN_TYPE_INNER != joinType ? COND_ACTION_STAY : (cxt.havaLeftCol && cxt.haveRightCol ? COND_ACTION_PUSH_JOIN : (cxt.havaLeftCol ? COND_ACTION_PUSH_LEFT_CHILD : COND_ACTION_PUSH_RIGHT_CHILD))); } static int32_t pushDownCondOptPartLogicCond(SJoinLogicNode* pJoin, SNode** pOnCond, SNode** pLeftChildCond, SNode** pRightChildCond) { SLogicConditionNode* pLogicCond = (SLogicConditionNode*)pJoin->node.pConditions; if (LOGIC_COND_TYPE_AND != pLogicCond->condType) { return TSDB_CODE_SUCCESS; } SNodeList* pLeftCols = ((SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 0))->pTargets; SNodeList* pRightCols = ((SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 1))->pTargets; int32_t code = TSDB_CODE_SUCCESS; SNodeList* pOnConds = NULL; SNodeList* pLeftChildConds = NULL; SNodeList* pRightChildConds = NULL; SNodeList* pRemainConds = NULL; SNode* pCond = NULL; FOREACH(pCond, pLogicCond->pParameterList) { ECondAction condAction = pushDownCondOptGetCondAction(pJoin->joinType, pLeftCols, pRightCols, pCond); if (COND_ACTION_PUSH_JOIN == condAction) { code = nodesListMakeAppend(&pOnConds, nodesCloneNode(pCond)); } else if (COND_ACTION_PUSH_LEFT_CHILD == condAction) { code = nodesListMakeAppend(&pLeftChildConds, nodesCloneNode(pCond)); } else if (COND_ACTION_PUSH_RIGHT_CHILD == condAction) { code = nodesListMakeAppend(&pRightChildConds, nodesCloneNode(pCond)); } else { code = nodesListMakeAppend(&pRemainConds, nodesCloneNode(pCond)); } if (TSDB_CODE_SUCCESS != code) { break; } } SNode* pTempOnCond = NULL; SNode* pTempLeftChildCond = NULL; SNode* pTempRightChildCond = NULL; SNode* pTempRemainCond = NULL; if (TSDB_CODE_SUCCESS == code) { code = nodesMergeConds(&pTempOnCond, &pOnConds); } if (TSDB_CODE_SUCCESS == code) { code = nodesMergeConds(&pTempLeftChildCond, &pLeftChildConds); } if (TSDB_CODE_SUCCESS == code) { code = nodesMergeConds(&pTempRightChildCond, &pRightChildConds); } if (TSDB_CODE_SUCCESS == code) { code = nodesMergeConds(&pTempRemainCond, &pRemainConds); } if (TSDB_CODE_SUCCESS == code) { *pOnCond = pTempOnCond; *pLeftChildCond = pTempLeftChildCond; *pRightChildCond = pTempRightChildCond; nodesDestroyNode(pJoin->node.pConditions); pJoin->node.pConditions = pTempRemainCond; } else { nodesDestroyList(pOnConds); nodesDestroyList(pLeftChildConds); nodesDestroyList(pRightChildConds); nodesDestroyList(pRemainConds); nodesDestroyNode(pTempOnCond); nodesDestroyNode(pTempLeftChildCond); nodesDestroyNode(pTempRightChildCond); nodesDestroyNode(pTempRemainCond); } return code; } static int32_t pushDownCondOptPartOpCond(SJoinLogicNode* pJoin, SNode** pOnCond, SNode** pLeftChildCond, SNode** pRightChildCond) { SNodeList* pLeftCols = ((SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 0))->pTargets; SNodeList* pRightCols = ((SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 1))->pTargets; ECondAction condAction = pushDownCondOptGetCondAction(pJoin->joinType, pLeftCols, pRightCols, pJoin->node.pConditions); if (COND_ACTION_STAY == condAction) { return TSDB_CODE_SUCCESS; } else if (COND_ACTION_PUSH_JOIN == condAction) { *pOnCond = pJoin->node.pConditions; } else if (COND_ACTION_PUSH_LEFT_CHILD == condAction) { *pLeftChildCond = pJoin->node.pConditions; } else if (COND_ACTION_PUSH_RIGHT_CHILD == condAction) { *pRightChildCond = pJoin->node.pConditions; } pJoin->node.pConditions = NULL; return TSDB_CODE_SUCCESS; } static int32_t pushDownCondOptPartCond(SJoinLogicNode* pJoin, SNode** pOnCond, SNode** pLeftChildCond, SNode** pRightChildCond) { if (QUERY_NODE_LOGIC_CONDITION == nodeType(pJoin->node.pConditions)) { return pushDownCondOptPartLogicCond(pJoin, pOnCond, pLeftChildCond, pRightChildCond); } else { return pushDownCondOptPartOpCond(pJoin, pOnCond, pLeftChildCond, pRightChildCond); } } static int32_t pushDownCondOptPushCondToOnCond(SOptimizeContext* pCxt, SJoinLogicNode* pJoin, SNode** pCond) { return pushDownCondOptAppendCond(&pJoin->pOnConditions, pCond); } static int32_t pushDownCondOptPushCondToChild(SOptimizeContext* pCxt, SLogicNode* pChild, SNode** pCond) { return pushDownCondOptAppendCond(&pChild->pConditions, pCond); } static bool pushDownCondOptIsPriKey(SNode* pNode, SNodeList* pTableCols) { if (QUERY_NODE_COLUMN != nodeType(pNode)) { return false; } SColumnNode* pCol = (SColumnNode*)pNode; if (PRIMARYKEY_TIMESTAMP_COL_ID != pCol->colId || TSDB_SYSTEM_TABLE == pCol->tableType) { return false; } return pushDownCondOptBelongThisTable(pNode, pTableCols); } static bool pushDownCondOptIsPriKeyEqualCond(SJoinLogicNode* pJoin, SNode* pCond) { if (QUERY_NODE_OPERATOR != nodeType(pCond)) { return false; } SOperatorNode* pOper = (SOperatorNode*)pCond; if (OP_TYPE_EQUAL != pOper->opType) { return false; } SNodeList* pLeftCols = ((SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 0))->pTargets; SNodeList* pRightCols = ((SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 1))->pTargets; if (pushDownCondOptIsPriKey(pOper->pLeft, pLeftCols)) { return pushDownCondOptIsPriKey(pOper->pRight, pRightCols); } else if (pushDownCondOptIsPriKey(pOper->pLeft, pRightCols)) { return pushDownCondOptIsPriKey(pOper->pRight, pLeftCols); } return false; } static bool pushDownCondOptContainPriKeyEqualCond(SJoinLogicNode* pJoin, SNode* pCond) { if (QUERY_NODE_LOGIC_CONDITION == nodeType(pCond)) { SLogicConditionNode* pLogicCond = (SLogicConditionNode*)pCond; if (LOGIC_COND_TYPE_AND != pLogicCond->condType) { return false; } bool hasPrimaryKeyEqualCond = false; SNode* pCond = NULL; FOREACH(pCond, pLogicCond->pParameterList) { if (pushDownCondOptContainPriKeyEqualCond(pJoin, pCond)) { hasPrimaryKeyEqualCond = true; break; } } return hasPrimaryKeyEqualCond; } else { return pushDownCondOptIsPriKeyEqualCond(pJoin, pCond); } } static int32_t pushDownCondOptCheckJoinOnCond(SOptimizeContext* pCxt, SJoinLogicNode* pJoin) { if (NULL == pJoin->pOnConditions) { return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PLAN_NOT_SUPPORT_CROSS_JOIN); } if (!pushDownCondOptContainPriKeyEqualCond(pJoin, pJoin->pOnConditions)) { return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PLAN_EXPECTED_TS_EQUAL); } return TSDB_CODE_SUCCESS; } static int32_t pushDownCondOptPartJoinOnCondLogicCond(SJoinLogicNode* pJoin, SNode** ppMergeCond, SNode** ppOnCond) { SLogicConditionNode* pLogicCond = (SLogicConditionNode*)(pJoin->pOnConditions); int32_t code = TSDB_CODE_SUCCESS; SNodeList* pOnConds = NULL; SNode* pCond = NULL; FOREACH(pCond, pLogicCond->pParameterList) { if (pushDownCondOptIsPriKeyEqualCond(pJoin, pCond)) { *ppMergeCond = nodesCloneNode(pCond); } else { code = nodesListMakeAppend(&pOnConds, nodesCloneNode(pCond)); } } SNode* pTempOnCond = NULL; if (TSDB_CODE_SUCCESS == code) { code = nodesMergeConds(&pTempOnCond, &pOnConds); } if (TSDB_CODE_SUCCESS == code && NULL != *ppMergeCond) { *ppOnCond = pTempOnCond; nodesDestroyNode(pJoin->pOnConditions); pJoin->pOnConditions = NULL; return TSDB_CODE_SUCCESS; } else { nodesDestroyList(pOnConds); nodesDestroyNode(pTempOnCond); return TSDB_CODE_PLAN_INTERNAL_ERROR; } } static int32_t pushDownCondOptPartJoinOnCond(SJoinLogicNode* pJoin, SNode** ppMergeCond, SNode** ppOnCond) { if (QUERY_NODE_LOGIC_CONDITION == nodeType(pJoin->pOnConditions) && LOGIC_COND_TYPE_AND == ((SLogicConditionNode*)(pJoin->pOnConditions))->condType) { return pushDownCondOptPartJoinOnCondLogicCond(pJoin, ppMergeCond, ppOnCond); } if (pushDownCondOptIsPriKeyEqualCond(pJoin, pJoin->pOnConditions)) { *ppMergeCond = nodesCloneNode(pJoin->pOnConditions); *ppOnCond = NULL; nodesDestroyNode(pJoin->pOnConditions); pJoin->pOnConditions = NULL; return TSDB_CODE_SUCCESS; } else { return TSDB_CODE_PLAN_INTERNAL_ERROR; } } static int32_t pushDownCondOptJoinExtractMergeCond(SOptimizeContext* pCxt, SJoinLogicNode* pJoin) { int32_t code = pushDownCondOptCheckJoinOnCond(pCxt, pJoin); SNode* pJoinMergeCond = NULL; SNode* pJoinOnCond = NULL; if (TSDB_CODE_SUCCESS == code) { code = pushDownCondOptPartJoinOnCond(pJoin, &pJoinMergeCond, &pJoinOnCond); } if (TSDB_CODE_SUCCESS == code) { pJoin->pMergeCondition = pJoinMergeCond; pJoin->pOnConditions = pJoinOnCond; } else { nodesDestroyNode(pJoinMergeCond); nodesDestroyNode(pJoinOnCond); } return code; } static int32_t pushDownCondOptDealJoin(SOptimizeContext* pCxt, SJoinLogicNode* pJoin) { if (OPTIMIZE_FLAG_TEST_MASK(pJoin->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE)) { return TSDB_CODE_SUCCESS; } if (NULL == pJoin->node.pConditions) { int32_t code = pushDownCondOptJoinExtractMergeCond(pCxt, pJoin); if (TSDB_CODE_SUCCESS == code) { OPTIMIZE_FLAG_SET_MASK(pJoin->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE); pCxt->optimized = true; } return code; } SNode* pOnCond = NULL; SNode* pLeftChildCond = NULL; SNode* pRightChildCond = NULL; int32_t code = pushDownCondOptPartCond(pJoin, &pOnCond, &pLeftChildCond, &pRightChildCond); if (TSDB_CODE_SUCCESS == code && NULL != pOnCond) { code = pushDownCondOptPushCondToOnCond(pCxt, pJoin, &pOnCond); } if (TSDB_CODE_SUCCESS == code && NULL != pLeftChildCond) { code = pushDownCondOptPushCondToChild(pCxt, (SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 0), &pLeftChildCond); } if (TSDB_CODE_SUCCESS == code && NULL != pRightChildCond) { code = pushDownCondOptPushCondToChild(pCxt, (SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 1), &pRightChildCond); } if (TSDB_CODE_SUCCESS == code) { code = pushDownCondOptJoinExtractMergeCond(pCxt, pJoin); } if (TSDB_CODE_SUCCESS == code) { OPTIMIZE_FLAG_SET_MASK(pJoin->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE); pCxt->optimized = true; } else { nodesDestroyNode(pOnCond); nodesDestroyNode(pLeftChildCond); nodesDestroyNode(pRightChildCond); } return code; } typedef struct SPartAggCondContext { SAggLogicNode* pAgg; bool hasAggFunc; } SPartAggCondContext; static EDealRes partAggCondHasAggFuncImpl(SNode* pNode, void* pContext) { SPartAggCondContext* pCxt = pContext; if (QUERY_NODE_COLUMN == nodeType(pNode)) { SNode* pAggFunc = NULL; FOREACH(pAggFunc, pCxt->pAgg->pAggFuncs) { if (strcmp(((SColumnNode*)pNode)->colName, ((SFunctionNode*)pAggFunc)->node.aliasName) == 0) { pCxt->hasAggFunc = true; return DEAL_RES_END; } } } return DEAL_RES_CONTINUE; } static int32_t partitionAggCondHasAggFunc(SAggLogicNode* pAgg, SNode* pCond) { SPartAggCondContext cxt = {.pAgg = pAgg, .hasAggFunc = false}; nodesWalkExpr(pCond, partAggCondHasAggFuncImpl, &cxt); return cxt.hasAggFunc; } static int32_t partitionAggCondConj(SAggLogicNode* pAgg, SNode** ppAggFuncCond, SNode** ppGroupKeyCond) { SLogicConditionNode* pLogicCond = (SLogicConditionNode*)pAgg->node.pConditions; int32_t code = TSDB_CODE_SUCCESS; SNodeList* pAggFuncConds = NULL; SNodeList* pGroupKeyConds = NULL; SNode* pCond = NULL; FOREACH(pCond, pLogicCond->pParameterList) { if (partitionAggCondHasAggFunc(pAgg, pCond)) { code = nodesListMakeAppend(&pAggFuncConds, nodesCloneNode(pCond)); } else { code = nodesListMakeAppend(&pGroupKeyConds, nodesCloneNode(pCond)); } if (TSDB_CODE_SUCCESS != code) { break; } } SNode* pTempAggFuncCond = NULL; SNode* pTempGroupKeyCond = NULL; if (TSDB_CODE_SUCCESS == code) { code = nodesMergeConds(&pTempAggFuncCond, &pAggFuncConds); } if (TSDB_CODE_SUCCESS == code) { code = nodesMergeConds(&pTempGroupKeyCond, &pGroupKeyConds); } if (TSDB_CODE_SUCCESS == code) { *ppAggFuncCond = pTempAggFuncCond; *ppGroupKeyCond = pTempGroupKeyCond; } else { nodesDestroyList(pAggFuncConds); nodesDestroyList(pGroupKeyConds); nodesDestroyNode(pTempAggFuncCond); nodesDestroyNode(pTempGroupKeyCond); } nodesDestroyNode(pAgg->node.pConditions); pAgg->node.pConditions = NULL; return code; } static int32_t partitionAggCond(SAggLogicNode* pAgg, SNode** ppAggFunCond, SNode** ppGroupKeyCond) { SNode* pAggNodeCond = pAgg->node.pConditions; if (QUERY_NODE_LOGIC_CONDITION == nodeType(pAggNodeCond) && LOGIC_COND_TYPE_AND == ((SLogicConditionNode*)(pAggNodeCond))->condType) { return partitionAggCondConj(pAgg, ppAggFunCond, ppGroupKeyCond); } if (partitionAggCondHasAggFunc(pAgg, pAggNodeCond)) { *ppAggFunCond = pAggNodeCond; } else { *ppGroupKeyCond = pAggNodeCond; } pAgg->node.pConditions = NULL; return TSDB_CODE_SUCCESS; } static int32_t pushCondToAggCond(SOptimizeContext* pCxt, SAggLogicNode* pAgg, SNode** pAggFuncCond) { return pushDownCondOptAppendCond(&pAgg->node.pConditions, pAggFuncCond); } typedef struct SRewriteAggGroupKeyCondContext { SAggLogicNode* pAgg; int32_t errCode; } SRewriteAggGroupKeyCondContext; static EDealRes rewriteAggGroupKeyCondForPushDownImpl(SNode** pNode, void* pContext) { SRewriteAggGroupKeyCondContext* pCxt = pContext; SAggLogicNode* pAgg = pCxt->pAgg; if (QUERY_NODE_COLUMN == nodeType(*pNode)) { SNode* pGroupKey = NULL; FOREACH(pGroupKey, pAgg->pGroupKeys) { SNode* pGroup = NULL; FOREACH(pGroup, ((SGroupingSetNode*)pGroupKey)->pParameterList) { if (0 == strcmp(((SExprNode*)pGroup)->aliasName, ((SColumnNode*)(*pNode))->colName)) { SNode* pExpr = nodesCloneNode(pGroup); if (pExpr == NULL) { pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY; return DEAL_RES_ERROR; } nodesDestroyNode(*pNode); *pNode = pExpr; return DEAL_RES_IGNORE_CHILD; } } } } return DEAL_RES_CONTINUE; } static int32_t rewriteAggGroupKeyCondForPushDown(SOptimizeContext* pCxt, SAggLogicNode* pAgg, SNode* pGroupKeyCond) { SRewriteAggGroupKeyCondContext cxt = {.pAgg = pAgg, .errCode = TSDB_CODE_SUCCESS}; nodesRewriteExpr(&pGroupKeyCond, rewriteAggGroupKeyCondForPushDownImpl, &cxt); return cxt.errCode; } static int32_t pushDownCondOptDealAgg(SOptimizeContext* pCxt, SAggLogicNode* pAgg) { if (NULL == pAgg->node.pConditions || OPTIMIZE_FLAG_TEST_MASK(pAgg->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE)) { return TSDB_CODE_SUCCESS; } // TODO: remove it after full implementation of pushing down to child if (1 != LIST_LENGTH(pAgg->node.pChildren)) { return TSDB_CODE_SUCCESS; } SNode* pAggFuncCond = NULL; SNode* pGroupKeyCond = NULL; int32_t code = partitionAggCond(pAgg, &pAggFuncCond, &pGroupKeyCond); if (TSDB_CODE_SUCCESS == code && NULL != pAggFuncCond) { code = pushCondToAggCond(pCxt, pAgg, &pAggFuncCond); } if (TSDB_CODE_SUCCESS == code && NULL != pGroupKeyCond) { code = rewriteAggGroupKeyCondForPushDown(pCxt, pAgg, pGroupKeyCond); } if (TSDB_CODE_SUCCESS == code && NULL != pGroupKeyCond) { SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pAgg->node.pChildren, 0); code = pushDownCondOptPushCondToChild(pCxt, pChild, &pGroupKeyCond); } if (TSDB_CODE_SUCCESS == code) { OPTIMIZE_FLAG_SET_MASK(pAgg->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE); pCxt->optimized = true; } else { nodesDestroyNode(pGroupKeyCond); nodesDestroyNode(pAggFuncCond); } return code; } typedef struct SRewriteProjCondContext { SProjectLogicNode* pProj; int32_t errCode; } SRewriteProjCondContext; static EDealRes rewriteProjectCondForPushDownImpl(SNode** ppNode, void* pContext) { SRewriteProjCondContext* pCxt = pContext; SProjectLogicNode* pProj = pCxt->pProj; if (QUERY_NODE_COLUMN == nodeType(*ppNode)) { SNode* pTarget = NULL; FOREACH(pTarget, pProj->node.pTargets) { if (nodesEqualNode(pTarget, *ppNode)) { SNode* pProjection = NULL; FOREACH(pProjection, pProj->pProjections) { if (0 == strcmp(((SExprNode*)pProjection)->aliasName, ((SColumnNode*)(*ppNode))->colName)) { SNode* pExpr = nodesCloneNode(pProjection); if (pExpr == NULL) { pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY; return DEAL_RES_ERROR; } nodesDestroyNode(*ppNode); *ppNode = pExpr; return DEAL_RES_IGNORE_CHILD; } // end if expr alias name equal column name } // end for each project } // end if target node equals cond column node } // end for each targets } return DEAL_RES_CONTINUE; } static int32_t rewriteProjectCondForPushDown(SOptimizeContext* pCxt, SProjectLogicNode* pProject, SNode** ppProjectCond) { SRewriteProjCondContext cxt = {.pProj = pProject, .errCode = TSDB_CODE_SUCCESS}; SNode* pProjectCond = pProject->node.pConditions; nodesRewriteExpr(&pProjectCond, rewriteProjectCondForPushDownImpl, &cxt); *ppProjectCond = pProjectCond; pProject->node.pConditions = NULL; return cxt.errCode; } static int32_t pushDownCondOptDealProject(SOptimizeContext* pCxt, SProjectLogicNode* pProject) { if (NULL == pProject->node.pConditions || OPTIMIZE_FLAG_TEST_MASK(pProject->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE)) { return TSDB_CODE_SUCCESS; } // TODO: remove it after full implementation of pushing down to child if (1 != LIST_LENGTH(pProject->node.pChildren)) { return TSDB_CODE_SUCCESS; } if (NULL != pProject->node.pLimit || NULL != pProject->node.pSlimit) { return TSDB_CODE_SUCCESS; } int32_t code = TSDB_CODE_SUCCESS; SNode* pProjCond = NULL; code = rewriteProjectCondForPushDown(pCxt, pProject, &pProjCond); if (TSDB_CODE_SUCCESS == code) { SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pProject->node.pChildren, 0); code = pushDownCondOptPushCondToChild(pCxt, pChild, &pProjCond); } if (TSDB_CODE_SUCCESS == code) { OPTIMIZE_FLAG_SET_MASK(pProject->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE); pCxt->optimized = true; } else { nodesDestroyNode(pProjCond); } return code; } static int32_t pushDownCondOptTrivialPushDown(SOptimizeContext* pCxt, SLogicNode* pLogicNode) { if (NULL == pLogicNode->pConditions || OPTIMIZE_FLAG_TEST_MASK(pLogicNode->optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE)) { return TSDB_CODE_SUCCESS; } SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pLogicNode->pChildren, 0); int32_t code = pushDownCondOptPushCondToChild(pCxt, pChild, &pLogicNode->pConditions); if (TSDB_CODE_SUCCESS == code) { OPTIMIZE_FLAG_SET_MASK(pLogicNode->optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE); pCxt->optimized = true; } return code; } static int32_t pushDownCondOptimizeImpl(SOptimizeContext* pCxt, SLogicNode* pLogicNode) { int32_t code = TSDB_CODE_SUCCESS; switch (nodeType(pLogicNode)) { case QUERY_NODE_LOGIC_PLAN_SCAN: code = pushDownCondOptDealScan(pCxt, (SScanLogicNode*)pLogicNode); break; case QUERY_NODE_LOGIC_PLAN_JOIN: code = pushDownCondOptDealJoin(pCxt, (SJoinLogicNode*)pLogicNode); break; case QUERY_NODE_LOGIC_PLAN_AGG: code = pushDownCondOptDealAgg(pCxt, (SAggLogicNode*)pLogicNode); break; case QUERY_NODE_LOGIC_PLAN_PROJECT: code = pushDownCondOptDealProject(pCxt, (SProjectLogicNode*)pLogicNode); break; case QUERY_NODE_LOGIC_PLAN_SORT: case QUERY_NODE_LOGIC_PLAN_PARTITION: code = pushDownCondOptTrivialPushDown(pCxt, pLogicNode); break; default: break; } if (TSDB_CODE_SUCCESS == code) { SNode* pChild = NULL; FOREACH(pChild, pLogicNode->pChildren) { code = pushDownCondOptimizeImpl(pCxt, (SLogicNode*)pChild); if (TSDB_CODE_SUCCESS != code) { break; } } } return code; } static int32_t pushDownCondOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) { return pushDownCondOptimizeImpl(pCxt, pLogicSubplan->pNode); } static bool sortPriKeyOptIsPriKeyOrderBy(SNodeList* pSortKeys) { if (1 != LIST_LENGTH(pSortKeys)) { return false; } SNode* pNode = ((SOrderByExprNode*)nodesListGetNode(pSortKeys, 0))->pExpr; return (QUERY_NODE_COLUMN == nodeType(pNode) ? (PRIMARYKEY_TIMESTAMP_COL_ID == ((SColumnNode*)pNode)->colId) : false); } static bool sortPriKeyOptMayBeOptimized(SLogicNode* pNode) { if (QUERY_NODE_LOGIC_PLAN_SORT != nodeType(pNode)) { return false; } SSortLogicNode* pSort = (SSortLogicNode*)pNode; if (pSort->groupSort || !sortPriKeyOptIsPriKeyOrderBy(pSort->pSortKeys) || 1 != LIST_LENGTH(pSort->node.pChildren)) { return false; } return true; } static int32_t sortPriKeyOptGetSequencingNodesImpl(SLogicNode* pNode, bool* pNotOptimize, SNodeList** pSequencingNodes) { switch (nodeType(pNode)) { case QUERY_NODE_LOGIC_PLAN_SCAN: { SScanLogicNode* pScan = (SScanLogicNode*)pNode; if (NULL != pScan->pGroupTags || TSDB_SYSTEM_TABLE == pScan->tableType) { *pNotOptimize = true; return TSDB_CODE_SUCCESS; } return nodesListMakeAppend(pSequencingNodes, (SNode*)pNode); } case QUERY_NODE_LOGIC_PLAN_JOIN: { int32_t code = sortPriKeyOptGetSequencingNodesImpl((SLogicNode*)nodesListGetNode(pNode->pChildren, 0), pNotOptimize, pSequencingNodes); if (TSDB_CODE_SUCCESS == code) { code = sortPriKeyOptGetSequencingNodesImpl((SLogicNode*)nodesListGetNode(pNode->pChildren, 1), pNotOptimize, pSequencingNodes); } return code; } case QUERY_NODE_LOGIC_PLAN_WINDOW: return nodesListMakeAppend(pSequencingNodes, (SNode*)pNode); case QUERY_NODE_LOGIC_PLAN_AGG: case QUERY_NODE_LOGIC_PLAN_PARTITION: *pNotOptimize = true; return TSDB_CODE_SUCCESS; default: break; } if (1 != LIST_LENGTH(pNode->pChildren)) { *pNotOptimize = true; return TSDB_CODE_SUCCESS; } return sortPriKeyOptGetSequencingNodesImpl((SLogicNode*)nodesListGetNode(pNode->pChildren, 0), pNotOptimize, pSequencingNodes); } static int32_t sortPriKeyOptGetSequencingNodes(SLogicNode* pNode, SNodeList** pSequencingNodes) { bool notOptimize = false; int32_t code = sortPriKeyOptGetSequencingNodesImpl(pNode, ¬Optimize, pSequencingNodes); if (TSDB_CODE_SUCCESS != code || notOptimize) { NODES_CLEAR_LIST(*pSequencingNodes); } return code; } static EOrder sortPriKeyOptGetPriKeyOrder(SSortLogicNode* pSort) { return ((SOrderByExprNode*)nodesListGetNode(pSort->pSortKeys, 0))->order; } static int32_t sortPriKeyOptApply(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan, SSortLogicNode* pSort, SNodeList* pSequencingNodes) { EOrder order = sortPriKeyOptGetPriKeyOrder(pSort); SNode* pSequencingNode = NULL; FOREACH(pSequencingNode, pSequencingNodes) { if (QUERY_NODE_LOGIC_PLAN_SCAN == nodeType(pSequencingNode)) { SScanLogicNode* pScan = (SScanLogicNode*)pSequencingNode; if ((ORDER_DESC == order && pScan->scanSeq[0] > 0) || (ORDER_ASC == order && pScan->scanSeq[1] > 0)) { TSWAP(pScan->scanSeq[0], pScan->scanSeq[1]); } if (TSDB_SUPER_TABLE == pScan->tableType) { pScan->scanType = SCAN_TYPE_TABLE_MERGE; pScan->node.resultDataOrder = DATA_ORDER_LEVEL_GLOBAL; pScan->node.requireDataOrder = DATA_ORDER_LEVEL_GLOBAL; } pScan->sortPrimaryKey = true; } else if (QUERY_NODE_LOGIC_PLAN_WINDOW == nodeType(pSequencingNode)) { ((SWindowLogicNode*)pSequencingNode)->outputTsOrder = order; } optSetParentOrder(((SLogicNode*)pSequencingNode)->pParent, order); } SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pSort->node.pChildren, 0); if (NULL == pSort->node.pParent) { TSWAP(pSort->node.pTargets, pChild->pTargets); } int32_t code = replaceLogicNode(pLogicSubplan, (SLogicNode*)pSort, pChild); if (TSDB_CODE_SUCCESS == code) { NODES_CLEAR_LIST(pSort->node.pChildren); nodesDestroyNode((SNode*)pSort); } pCxt->optimized = true; return code; } static int32_t sortPrimaryKeyOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan, SSortLogicNode* pSort) { SNodeList* pSequencingNodes = NULL; int32_t code = sortPriKeyOptGetSequencingNodes((SLogicNode*)nodesListGetNode(pSort->node.pChildren, 0), &pSequencingNodes); if (TSDB_CODE_SUCCESS == code && NULL != pSequencingNodes) { code = sortPriKeyOptApply(pCxt, pLogicSubplan, pSort, pSequencingNodes); } nodesClearList(pSequencingNodes); return code; } static int32_t sortPrimaryKeyOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) { SSortLogicNode* pSort = (SSortLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, sortPriKeyOptMayBeOptimized); if (NULL == pSort) { return TSDB_CODE_SUCCESS; } return sortPrimaryKeyOptimizeImpl(pCxt, pLogicSubplan, pSort); } static bool smaIndexOptMayBeOptimized(SLogicNode* pNode) { if (QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(pNode) || NULL == pNode->pParent || QUERY_NODE_LOGIC_PLAN_WINDOW != nodeType(pNode->pParent) || WINDOW_TYPE_INTERVAL != ((SWindowLogicNode*)pNode->pParent)->winType) { return false; } SScanLogicNode* pScan = (SScanLogicNode*)pNode; if (NULL == pScan->pSmaIndexes || NULL != pScan->node.pConditions) { return false; } return true; } static int32_t smaIndexOptCreateSmaScan(SScanLogicNode* pScan, STableIndexInfo* pIndex, SNodeList* pCols, SLogicNode** pOutput) { SScanLogicNode* pSmaScan = (SScanLogicNode*)nodesMakeNode(QUERY_NODE_LOGIC_PLAN_SCAN); if (NULL == pSmaScan) { return TSDB_CODE_OUT_OF_MEMORY; } pSmaScan->pScanCols = pCols; pSmaScan->tableType = TSDB_SUPER_TABLE; pSmaScan->tableId = pIndex->dstTbUid; pSmaScan->stableId = pIndex->dstTbUid; pSmaScan->scanType = SCAN_TYPE_TABLE; pSmaScan->scanSeq[0] = pScan->scanSeq[0]; pSmaScan->scanSeq[1] = pScan->scanSeq[1]; pSmaScan->scanRange = pScan->scanRange; pSmaScan->dataRequired = FUNC_DATA_REQUIRED_DATA_LOAD; pSmaScan->pVgroupList = taosMemoryCalloc(1, sizeof(SVgroupsInfo) + sizeof(SVgroupInfo)); pSmaScan->node.pTargets = nodesCloneList(pCols); if (NULL == pSmaScan->pVgroupList || NULL == pSmaScan->node.pTargets) { nodesDestroyNode((SNode*)pSmaScan); return TSDB_CODE_OUT_OF_MEMORY; } pSmaScan->pVgroupList->numOfVgroups = 1; pSmaScan->pVgroupList->vgroups[0].vgId = pIndex->dstVgId; memcpy(&(pSmaScan->pVgroupList->vgroups[0].epSet), &pIndex->epSet, sizeof(SEpSet)); *pOutput = (SLogicNode*)pSmaScan; return TSDB_CODE_SUCCESS; } static bool smaIndexOptEqualInterval(SScanLogicNode* pScan, SWindowLogicNode* pWindow, STableIndexInfo* pIndex) { if (pWindow->interval != pIndex->interval || pWindow->intervalUnit != pIndex->intervalUnit || pWindow->offset != pIndex->offset || pWindow->sliding != pIndex->sliding || pWindow->slidingUnit != pIndex->slidingUnit) { return false; } if (IS_TSWINDOW_SPECIFIED(pScan->scanRange)) { SInterval interval = {.interval = pIndex->interval, .intervalUnit = pIndex->intervalUnit, .offset = pIndex->offset, .offsetUnit = TIME_UNIT_MILLISECOND, .sliding = pIndex->sliding, .slidingUnit = pIndex->slidingUnit, .precision = pScan->node.precision}; return (pScan->scanRange.skey == taosTimeTruncate(pScan->scanRange.skey, &interval, pScan->node.precision)) && (pScan->scanRange.ekey + 1 == taosTimeTruncate(pScan->scanRange.ekey + 1, &interval, pScan->node.precision)); } return true; } static SNode* smaIndexOptCreateSmaCol(SNode* pFunc, uint64_t tableId, int32_t colId) { SColumnNode* pCol = (SColumnNode*)nodesMakeNode(QUERY_NODE_COLUMN); if (NULL == pCol) { return NULL; } pCol->tableId = tableId; pCol->tableType = TSDB_SUPER_TABLE; pCol->colId = colId; pCol->colType = COLUMN_TYPE_COLUMN; strcpy(pCol->colName, ((SExprNode*)pFunc)->aliasName); pCol->node.resType = ((SExprNode*)pFunc)->resType; strcpy(pCol->node.aliasName, ((SExprNode*)pFunc)->aliasName); return (SNode*)pCol; } static int32_t smaIndexOptFindSmaFunc(SNode* pQueryFunc, SNodeList* pSmaFuncs) { int32_t index = 0; SNode* pSmaFunc = NULL; FOREACH(pSmaFunc, pSmaFuncs) { if (nodesEqualNode(pQueryFunc, pSmaFunc)) { return index; } ++index; } return -1; } static int32_t smaIndexOptCreateSmaCols(SNodeList* pFuncs, uint64_t tableId, SNodeList* pSmaFuncs, SNodeList** pOutput, int32_t* pWStrartIndex) { SNodeList* pCols = NULL; SNode* pFunc = NULL; int32_t code = TSDB_CODE_SUCCESS; int32_t index = 0; int32_t smaFuncIndex = -1; *pWStrartIndex = -1; FOREACH(pFunc, pFuncs) { if (FUNCTION_TYPE_WSTART == ((SFunctionNode*)pFunc)->funcType) { *pWStrartIndex = index; } smaFuncIndex = smaIndexOptFindSmaFunc(pFunc, pSmaFuncs); if (smaFuncIndex < 0) { break; } else { code = nodesListMakeStrictAppend(&pCols, smaIndexOptCreateSmaCol(pFunc, tableId, smaFuncIndex + 1)); if (TSDB_CODE_SUCCESS != code) { break; } } ++index; } if (TSDB_CODE_SUCCESS == code && smaFuncIndex >= 0) { *pOutput = pCols; } else { nodesDestroyList(pCols); } return code; } static int32_t smaIndexOptCouldApplyIndex(SScanLogicNode* pScan, STableIndexInfo* pIndex, SNodeList** pCols, int32_t* pWStrartIndex) { SWindowLogicNode* pWindow = (SWindowLogicNode*)pScan->node.pParent; if (!smaIndexOptEqualInterval(pScan, pWindow, pIndex)) { return TSDB_CODE_SUCCESS; } SNodeList* pSmaFuncs = NULL; int32_t code = nodesStringToList(pIndex->expr, &pSmaFuncs); if (TSDB_CODE_SUCCESS == code) { code = smaIndexOptCreateSmaCols(pWindow->pFuncs, pIndex->dstTbUid, pSmaFuncs, pCols, pWStrartIndex); } nodesDestroyList(pSmaFuncs); return code; } static int32_t smaIndexOptApplyIndex(SLogicSubplan* pLogicSubplan, SScanLogicNode* pScan, STableIndexInfo* pIndex, SNodeList* pSmaCols, int32_t wstrartIndex) { SLogicNode* pSmaScan = NULL; int32_t code = smaIndexOptCreateSmaScan(pScan, pIndex, pSmaCols, &pSmaScan); if (TSDB_CODE_SUCCESS == code) { code = replaceLogicNode(pLogicSubplan, pScan->node.pParent, pSmaScan); } return code; } static void smaIndexOptDestroySmaIndex(void* p) { taosMemoryFree(((STableIndexInfo*)p)->expr); } static int32_t smaIndexOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan, SScanLogicNode* pScan) { int32_t code = TSDB_CODE_SUCCESS; int32_t nindexes = taosArrayGetSize(pScan->pSmaIndexes); for (int32_t i = 0; i < nindexes; ++i) { STableIndexInfo* pIndex = taosArrayGet(pScan->pSmaIndexes, i); SNodeList* pSmaCols = NULL; int32_t wstrartIndex = -1; code = smaIndexOptCouldApplyIndex(pScan, pIndex, &pSmaCols, &wstrartIndex); if (TSDB_CODE_SUCCESS == code && NULL != pSmaCols) { code = smaIndexOptApplyIndex(pLogicSubplan, pScan, pIndex, pSmaCols, wstrartIndex); taosArrayDestroyEx(pScan->pSmaIndexes, smaIndexOptDestroySmaIndex); pScan->pSmaIndexes = NULL; pCxt->optimized = true; break; } } return code; } static int32_t smaIndexOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) { SScanLogicNode* pScan = (SScanLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, smaIndexOptMayBeOptimized); if (NULL == pScan) { return TSDB_CODE_SUCCESS; } return smaIndexOptimizeImpl(pCxt, pLogicSubplan, pScan); } static EDealRes partTagsOptHasColImpl(SNode* pNode, void* pContext) { if (QUERY_NODE_COLUMN == nodeType(pNode)) { if (COLUMN_TYPE_TAG != ((SColumnNode*)pNode)->colType && COLUMN_TYPE_TBNAME != ((SColumnNode*)pNode)->colType) { *(bool*)pContext = true; return DEAL_RES_END; } } return DEAL_RES_CONTINUE; } static bool planOptNodeListHasCol(SNodeList* pKeys) { bool hasCol = false; nodesWalkExprs(pKeys, partTagsOptHasColImpl, &hasCol); return hasCol; } static EDealRes partTagsOptHasTbname(SNode* pNode, void* pContext) { if (QUERY_NODE_COLUMN == nodeType(pNode)) { if (COLUMN_TYPE_TBNAME == ((SColumnNode*)pNode)->colType) { *(bool*)pContext = true; return DEAL_RES_END; } } return DEAL_RES_CONTINUE; } static bool planOptNodeListHasTbname(SNodeList* pKeys) { bool hasCol = false; nodesWalkExprs(pKeys, partTagsOptHasTbname, &hasCol); return hasCol; } static bool partTagsIsOptimizableNode(SLogicNode* pNode) { return ((QUERY_NODE_LOGIC_PLAN_PARTITION == nodeType(pNode) || (QUERY_NODE_LOGIC_PLAN_AGG == nodeType(pNode) && NULL != ((SAggLogicNode*)pNode)->pGroupKeys && NULL != ((SAggLogicNode*)pNode)->pAggFuncs)) && 1 == LIST_LENGTH(pNode->pChildren) && QUERY_NODE_LOGIC_PLAN_SCAN == nodeType(nodesListGetNode(pNode->pChildren, 0))); } static SNodeList* partTagsGetPartKeys(SLogicNode* pNode) { if (QUERY_NODE_LOGIC_PLAN_PARTITION == nodeType(pNode)) { return ((SPartitionLogicNode*)pNode)->pPartitionKeys; } else { return ((SAggLogicNode*)pNode)->pGroupKeys; } } static SNodeList* partTagsGetFuncs(SLogicNode* pNode) { if (QUERY_NODE_LOGIC_PLAN_PARTITION == nodeType(pNode)) { return NULL; } else { return ((SAggLogicNode*)pNode)->pAggFuncs; } } static bool partTagsOptAreSupportedFuncs(SNodeList* pFuncs) { SNode* pFunc = NULL; FOREACH(pFunc, pFuncs) { if (fmIsIndefiniteRowsFunc(((SFunctionNode*)pFunc)->funcId) && !fmIsSelectFunc(((SFunctionNode*)pFunc)->funcId)) { return false; } } return true; } static bool partTagsOptMayBeOptimized(SLogicNode* pNode) { if (!partTagsIsOptimizableNode(pNode)) { return false; } return !planOptNodeListHasCol(partTagsGetPartKeys(pNode)) && partTagsOptAreSupportedFuncs(partTagsGetFuncs(pNode)); } static int32_t partTagsOptRebuildTbanme(SNodeList* pPartKeys) { int32_t code = TSDB_CODE_SUCCESS; nodesRewriteExprs(pPartKeys, optRebuildTbanme, &code); return code; } // todo refact: just to mask compilation warnings static void partTagsSetAlias(char* pAlias, int32_t len, const char* pTableAlias, const char* pColName) { snprintf(pAlias, len, "%s.%s", pTableAlias, pColName); } static SNode* partTagsCreateWrapperFunc(const char* pFuncName, SNode* pNode) { SFunctionNode* pFunc = (SFunctionNode*)nodesMakeNode(QUERY_NODE_FUNCTION); if (NULL == pFunc) { return NULL; } snprintf(pFunc->functionName, sizeof(pFunc->functionName), "%s", pFuncName); if (QUERY_NODE_COLUMN == nodeType(pNode) && COLUMN_TYPE_TBNAME != ((SColumnNode*)pNode)->colType) { SColumnNode* pCol = (SColumnNode*)pNode; partTagsSetAlias(pFunc->node.aliasName, sizeof(pFunc->node.aliasName), pCol->tableAlias, pCol->colName); } else { strcpy(pFunc->node.aliasName, ((SExprNode*)pNode)->aliasName); } int32_t code = nodesListMakeStrictAppend(&pFunc->pParameterList, nodesCloneNode(pNode)); if (TSDB_CODE_SUCCESS == code) { code = fmGetFuncInfo(pFunc, NULL, 0); } if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pFunc); return NULL; } return (SNode*)pFunc; } static bool partTagsHasIndefRowsSelectFunc(SNodeList* pFuncs) { SNode* pFunc = NULL; FOREACH(pFunc, pFuncs) { if (fmIsIndefiniteRowsFunc(((SFunctionNode*)pFunc)->funcId)) { return true; } } return false; } static int32_t partTagsRewriteGroupTagsToFuncs(SNodeList* pGroupTags, int32_t start, SNodeList* pAggFuncs) { bool hasIndefRowsSelectFunc = partTagsHasIndefRowsSelectFunc(pAggFuncs); int32_t code = TSDB_CODE_SUCCESS; int32_t index = 0; SNode* pNode = NULL; FOREACH(pNode, pGroupTags) { if (index++ < start) { continue; } if (hasIndefRowsSelectFunc) { code = nodesListStrictAppend(pAggFuncs, partTagsCreateWrapperFunc("_select_value", pNode)); } else { code = nodesListStrictAppend(pAggFuncs, partTagsCreateWrapperFunc("_group_key", pNode)); } if (TSDB_CODE_SUCCESS != code) { break; } } return code; } static int32_t partTagsOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) { SLogicNode* pNode = optFindPossibleNode(pLogicSubplan->pNode, partTagsOptMayBeOptimized); if (NULL == pNode) { return TSDB_CODE_SUCCESS; } int32_t code = TSDB_CODE_SUCCESS; SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(pNode->pChildren, 0); if (QUERY_NODE_LOGIC_PLAN_PARTITION == nodeType(pNode)) { TSWAP(((SPartitionLogicNode*)pNode)->pPartitionKeys, pScan->pGroupTags); TSWAP(((SPartitionLogicNode*)pNode)->pTags, pScan->pTags); TSWAP(((SPartitionLogicNode*)pNode)->pSubtable, pScan->pSubtable); int32_t code = replaceLogicNode(pLogicSubplan, pNode, (SLogicNode*)pScan); if (TSDB_CODE_SUCCESS == code) { code = adjustLogicNodeDataRequirement((SLogicNode*)pScan, pNode->resultDataOrder); } if (TSDB_CODE_SUCCESS == code) { NODES_CLEAR_LIST(pNode->pChildren); nodesDestroyNode((SNode*)pNode); } } else { SAggLogicNode* pAgg = (SAggLogicNode*)pNode; int32_t start = -1; SNode* pGroupKey = NULL; FOREACH(pGroupKey, pAgg->pGroupKeys) { SNode* pGroupExpr = nodesListGetNode(((SGroupingSetNode*)pGroupKey)->pParameterList, 0); if (NULL != pScan->pGroupTags) { SNode* pGroupTag = NULL; FOREACH(pGroupTag, pScan->pGroupTags) { if (nodesEqualNode(pGroupTag, pGroupExpr)) { continue; } } } if (start < 0) { start = LIST_LENGTH(pScan->pGroupTags); } code = nodesListMakeStrictAppend(&pScan->pGroupTags, nodesCloneNode(pGroupExpr)); if (TSDB_CODE_SUCCESS != code) { break; } } NODES_DESTORY_LIST(pAgg->pGroupKeys); if (TSDB_CODE_SUCCESS == code && start >= 0) { code = partTagsRewriteGroupTagsToFuncs(pScan->pGroupTags, start, pAgg->pAggFuncs); } } if (TSDB_CODE_SUCCESS == code) { code = partTagsOptRebuildTbanme(pScan->pGroupTags); } pCxt->optimized = true; return code; } static bool eliminateProjOptCheckProjColumnNames(SProjectLogicNode* pProjectNode) { SHashObj* pProjColNameHash = taosHashInit(16, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_NO_LOCK); SNode* pProjection; FOREACH(pProjection, pProjectNode->pProjections) { char* projColumnName = ((SColumnNode*)pProjection)->colName; int32_t* pExist = taosHashGet(pProjColNameHash, projColumnName, strlen(projColumnName)); if (NULL != pExist) { taosHashCleanup(pProjColNameHash); return false; } else { int32_t exist = 1; taosHashPut(pProjColNameHash, projColumnName, strlen(projColumnName), &exist, sizeof(exist)); } } taosHashCleanup(pProjColNameHash); return true; } static bool eliminateProjOptMayBeOptimized(SLogicNode* pNode) { // TODO: enable this optimization after new mechanising that map projection and targets of project node if (NULL != pNode->pParent) { return false; } // Super table scan requires project operator to merge packets to improve performance. if (QUERY_NODE_LOGIC_PLAN_PROJECT != nodeType(pNode) || 1 != LIST_LENGTH(pNode->pChildren) || (QUERY_NODE_LOGIC_PLAN_SCAN == nodeType(nodesListGetNode(pNode->pChildren, 0)) && TSDB_SUPER_TABLE == ((SScanLogicNode*)nodesListGetNode(pNode->pChildren, 0))->tableType)) { return false; } SProjectLogicNode* pProjectNode = (SProjectLogicNode*)pNode; if (NULL != pProjectNode->node.pLimit || NULL != pProjectNode->node.pSlimit || NULL != pProjectNode->node.pConditions) { return false; } SNode* pProjection; FOREACH(pProjection, pProjectNode->pProjections) { SExprNode* pExprNode = (SExprNode*)pProjection; if (QUERY_NODE_COLUMN != nodeType(pExprNode)) { return false; } } return eliminateProjOptCheckProjColumnNames(pProjectNode); } typedef struct CheckNewChildTargetsCxt { SNodeList* pNewChildTargets; bool canUse; } CheckNewChildTargetsCxt; static EDealRes eliminateProjOptCanUseNewChildTargetsImpl(SNode* pNode, void* pContext) { if (QUERY_NODE_COLUMN == nodeType(pNode)) { CheckNewChildTargetsCxt* pCxt = pContext; SNode* pTarget = NULL; FOREACH(pTarget, pCxt->pNewChildTargets) { if (nodesEqualNode(pTarget, pNode)) { pCxt->canUse = true; return DEAL_RES_CONTINUE; } } pCxt->canUse = false; return DEAL_RES_END; } return DEAL_RES_CONTINUE; } static bool eliminateProjOptCanChildConditionUseChildTargets(SLogicNode* pChild, SNodeList* pNewChildTargets) { if (NULL != pChild->pConditions) { CheckNewChildTargetsCxt cxt = {.pNewChildTargets = pNewChildTargets, .canUse = false}; nodesWalkExpr(pChild->pConditions, eliminateProjOptCanUseNewChildTargetsImpl, &cxt); if (!cxt.canUse) return false; } if (QUERY_NODE_LOGIC_PLAN_JOIN == nodeType(pChild) && NULL != ((SJoinLogicNode*)pChild)->pOnConditions) { SJoinLogicNode* pJoinLogicNode = (SJoinLogicNode*)pChild; CheckNewChildTargetsCxt cxt = {.pNewChildTargets = pNewChildTargets, .canUse = false}; nodesWalkExpr(pJoinLogicNode->pOnConditions, eliminateProjOptCanUseNewChildTargetsImpl, &cxt); if (!cxt.canUse) return false; } return true; } static void alignProjectionWithTarget(SLogicNode* pNode) { if (QUERY_NODE_LOGIC_PLAN_PROJECT != pNode->type) { return; } SProjectLogicNode* pProjectNode = (SProjectLogicNode*)pNode; SNode* pProjection = NULL; FOREACH(pProjection, pProjectNode->pProjections) { SNode* pTarget = NULL; bool keep = false; FOREACH(pTarget, pNode->pTargets) { if (0 == strcmp(((SColumnNode*)pProjection)->node.aliasName, ((SColumnNode*)pTarget)->colName)) { keep = true; break; } } if (!keep) { nodesListErase(pProjectNode->pProjections, cell); } } } static int32_t eliminateProjOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan, SProjectLogicNode* pProjectNode) { SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pProjectNode->node.pChildren, 0); SNodeList* pNewChildTargets = nodesMakeList(); SNode* pProjection = NULL; FOREACH(pProjection, pProjectNode->pProjections) { SNode* pChildTarget = NULL; FOREACH(pChildTarget, pChild->pTargets) { if (0 == strcmp(((SColumnNode*)pProjection)->colName, ((SColumnNode*)pChildTarget)->colName)) { nodesListAppend(pNewChildTargets, nodesCloneNode(pChildTarget)); break; } } } if (eliminateProjOptCanChildConditionUseChildTargets(pChild, pNewChildTargets)) { nodesDestroyList(pChild->pTargets); pChild->pTargets = pNewChildTargets; } else { nodesDestroyList(pNewChildTargets); return TSDB_CODE_SUCCESS; } int32_t code = replaceLogicNode(pLogicSubplan, (SLogicNode*)pProjectNode, pChild); if (TSDB_CODE_SUCCESS == code) { NODES_CLEAR_LIST(pProjectNode->node.pChildren); nodesDestroyNode((SNode*)pProjectNode); // if pChild is a project logic node, remove its projection which is not reference by its target. alignProjectionWithTarget(pChild); } pCxt->optimized = true; return code; } static int32_t eliminateProjOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) { SProjectLogicNode* pProjectNode = (SProjectLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, eliminateProjOptMayBeOptimized); if (NULL == pProjectNode) { return TSDB_CODE_SUCCESS; } return eliminateProjOptimizeImpl(pCxt, pLogicSubplan, pProjectNode); } static bool rewriteTailOptMayBeOptimized(SLogicNode* pNode) { return QUERY_NODE_LOGIC_PLAN_INDEF_ROWS_FUNC == nodeType(pNode) && ((SIndefRowsFuncLogicNode*)pNode)->isTailFunc; } static SNode* rewriteTailOptCreateOrderByExpr(SNode* pSortKey) { SOrderByExprNode* pOrder = (SOrderByExprNode*)nodesMakeNode(QUERY_NODE_ORDER_BY_EXPR); if (NULL == pOrder) { return NULL; } pOrder->order = ORDER_DESC; pOrder->pExpr = nodesCloneNode(pSortKey); if (NULL == pOrder->pExpr) { nodesDestroyNode((SNode*)pOrder); return NULL; } return (SNode*)pOrder; } static int32_t rewriteTailOptCreateLimit(SNode* pLimit, SNode* pOffset, SNode** pOutput) { SLimitNode* pLimitNode = (SLimitNode*)nodesMakeNode(QUERY_NODE_LIMIT); if (NULL == pLimitNode) { return TSDB_CODE_OUT_OF_MEMORY; } pLimitNode->limit = NULL == pLimit ? -1 : ((SValueNode*)pLimit)->datum.i; pLimitNode->offset = NULL == pOffset ? 0 : ((SValueNode*)pOffset)->datum.i; *pOutput = (SNode*)pLimitNode; return TSDB_CODE_SUCCESS; } static bool rewriteTailOptNeedGroupSort(SIndefRowsFuncLogicNode* pIndef) { if (1 != LIST_LENGTH(pIndef->node.pChildren)) { return false; } SNode* pChild = nodesListGetNode(pIndef->node.pChildren, 0); return QUERY_NODE_LOGIC_PLAN_PARTITION == nodeType(pChild) || (QUERY_NODE_LOGIC_PLAN_SCAN == nodeType(pChild) && NULL != ((SScanLogicNode*)pChild)->pGroupTags); } static int32_t rewriteTailOptCreateSort(SIndefRowsFuncLogicNode* pIndef, SLogicNode** pOutput) { SSortLogicNode* pSort = (SSortLogicNode*)nodesMakeNode(QUERY_NODE_LOGIC_PLAN_SORT); if (NULL == pSort) { return TSDB_CODE_OUT_OF_MEMORY; } pSort->groupSort = rewriteTailOptNeedGroupSort(pIndef); TSWAP(pSort->node.pChildren, pIndef->node.pChildren); optResetParent((SLogicNode*)pSort); pSort->node.precision = pIndef->node.precision; SFunctionNode* pTail = NULL; SNode* pFunc = NULL; FOREACH(pFunc, pIndef->pFuncs) { if (FUNCTION_TYPE_TAIL == ((SFunctionNode*)pFunc)->funcType) { pTail = (SFunctionNode*)pFunc; break; } } // tail(expr, [limit, offset,] _rowts) int32_t rowtsIndex = LIST_LENGTH(pTail->pParameterList) - 1; int32_t code = nodesListMakeStrictAppend( &pSort->pSortKeys, rewriteTailOptCreateOrderByExpr(nodesListGetNode(pTail->pParameterList, rowtsIndex))); if (TSDB_CODE_SUCCESS == code) { pSort->node.pTargets = nodesCloneList(((SLogicNode*)nodesListGetNode(pSort->node.pChildren, 0))->pTargets); if (NULL == pSort->node.pTargets) { code = TSDB_CODE_OUT_OF_MEMORY; } } if (TSDB_CODE_SUCCESS == code) { *pOutput = (SLogicNode*)pSort; } else { nodesDestroyNode((SNode*)pSort); } return code; } static SNode* rewriteTailOptCreateProjectExpr(SFunctionNode* pFunc) { SNode* pExpr = nodesCloneNode(nodesListGetNode(pFunc->pParameterList, 0)); if (NULL == pExpr) { return NULL; } strcpy(((SExprNode*)pExpr)->aliasName, pFunc->node.aliasName); return pExpr; } static int32_t rewriteTailOptCreateProject(SIndefRowsFuncLogicNode* pIndef, SLogicNode** pOutput) { SProjectLogicNode* pProject = (SProjectLogicNode*)nodesMakeNode(QUERY_NODE_LOGIC_PLAN_PROJECT); if (NULL == pProject) { return TSDB_CODE_OUT_OF_MEMORY; } TSWAP(pProject->node.pTargets, pIndef->node.pTargets); pProject->node.precision = pIndef->node.precision; int32_t code = TSDB_CODE_SUCCESS; SFunctionNode* pTail = NULL; SNode* pFunc = NULL; FOREACH(pFunc, pIndef->pFuncs) { code = nodesListMakeStrictAppend(&pProject->pProjections, rewriteTailOptCreateProjectExpr((SFunctionNode*)pFunc)); if (TSDB_CODE_SUCCESS != code) { break; } if (FUNCTION_TYPE_TAIL == ((SFunctionNode*)pFunc)->funcType) { pTail = (SFunctionNode*)pFunc; } } // tail(expr, [limit, offset,] _rowts) int32_t limitIndex = LIST_LENGTH(pTail->pParameterList) > 2 ? 1 : -1; int32_t offsetIndex = LIST_LENGTH(pTail->pParameterList) > 3 ? 2 : -1; if (TSDB_CODE_SUCCESS == code) { code = rewriteTailOptCreateLimit(limitIndex < 0 ? NULL : nodesListGetNode(pTail->pParameterList, limitIndex), offsetIndex < 0 ? NULL : nodesListGetNode(pTail->pParameterList, offsetIndex), &pProject->node.pLimit); } if (TSDB_CODE_SUCCESS == code) { *pOutput = (SLogicNode*)pProject; } else { nodesDestroyNode((SNode*)pProject); } return code; } static int32_t rewriteTailOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan, SIndefRowsFuncLogicNode* pIndef) { SLogicNode* pSort = NULL; SLogicNode* pProject = NULL; int32_t code = rewriteTailOptCreateSort(pIndef, &pSort); if (TSDB_CODE_SUCCESS == code) { code = rewriteTailOptCreateProject(pIndef, &pProject); } if (TSDB_CODE_SUCCESS == code) { code = nodesListMakeAppend(&pProject->pChildren, (SNode*)pSort); pSort->pParent = pProject; pSort = NULL; } if (TSDB_CODE_SUCCESS == code) { code = replaceLogicNode(pLogicSubplan, (SLogicNode*)pIndef, pProject); } if (TSDB_CODE_SUCCESS == code) { nodesDestroyNode((SNode*)pIndef); } else { nodesDestroyNode((SNode*)pSort); nodesDestroyNode((SNode*)pProject); } pCxt->optimized = true; return code; } static int32_t rewriteTailOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) { SIndefRowsFuncLogicNode* pIndef = (SIndefRowsFuncLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, rewriteTailOptMayBeOptimized); if (NULL == pIndef) { return TSDB_CODE_SUCCESS; } return rewriteTailOptimizeImpl(pCxt, pLogicSubplan, pIndef); } static bool eliminateSetOpMayBeOptimized(SLogicNode* pNode) { SLogicNode* pParent = pNode->pParent; if (NULL == pParent || QUERY_NODE_LOGIC_PLAN_AGG != nodeType(pParent) && QUERY_NODE_LOGIC_PLAN_PROJECT != nodeType(pParent) || LIST_LENGTH(pParent->pChildren) < 2) { return false; } if (nodeType(pNode) != nodeType(pNode->pParent) || LIST_LENGTH(pNode->pChildren) < 2) { return false; } return true; } static int32_t eliminateSetOpOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan, SLogicNode* pSetOpNode) { SNode* pSibling; FOREACH(pSibling, pSetOpNode->pParent->pChildren) { if (nodesEqualNode(pSibling, (SNode*)pSetOpNode)) { SNode* pChild; FOREACH(pChild, pSetOpNode->pChildren) { ((SLogicNode*)pChild)->pParent = pSetOpNode->pParent; } INSERT_LIST(pSetOpNode->pParent->pChildren, pSetOpNode->pChildren); pSetOpNode->pChildren = NULL; ERASE_NODE(pSetOpNode->pParent->pChildren); pCxt->optimized = true; return TSDB_CODE_SUCCESS; } } return TSDB_CODE_PLAN_INTERNAL_ERROR; } static int32_t eliminateSetOpOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) { SLogicNode* pSetOpNode = optFindPossibleNode(pLogicSubplan->pNode, eliminateSetOpMayBeOptimized); if (NULL == pSetOpNode) { return TSDB_CODE_SUCCESS; } return eliminateSetOpOptimizeImpl(pCxt, pLogicSubplan, pSetOpNode); } static bool rewriteUniqueOptMayBeOptimized(SLogicNode* pNode) { return QUERY_NODE_LOGIC_PLAN_INDEF_ROWS_FUNC == nodeType(pNode) && ((SIndefRowsFuncLogicNode*)pNode)->isUniqueFunc; } static SNode* rewriteUniqueOptCreateGroupingSet(SNode* pExpr) { SGroupingSetNode* pGroupingSet = (SGroupingSetNode*)nodesMakeNode(QUERY_NODE_GROUPING_SET); if (NULL == pGroupingSet) { return NULL; } pGroupingSet->groupingSetType = GP_TYPE_NORMAL; SExprNode* pGroupExpr = (SExprNode*)nodesCloneNode(pExpr); if (TSDB_CODE_SUCCESS != nodesListMakeStrictAppend(&pGroupingSet->pParameterList, (SNode*)pGroupExpr)) { nodesDestroyNode((SNode*)pGroupingSet); return NULL; } return (SNode*)pGroupingSet; } static SNode* rewriteUniqueOptCreateFirstFunc(SFunctionNode* pSelectValue, SNode* pCol) { SFunctionNode* pFunc = (SFunctionNode*)nodesMakeNode(QUERY_NODE_FUNCTION); if (NULL == pFunc) { return NULL; } strcpy(pFunc->functionName, "first"); if (NULL != pSelectValue) { strcpy(pFunc->node.aliasName, pSelectValue->node.aliasName); } else { int64_t pointer = (int64_t)pFunc; snprintf(pFunc->node.aliasName, sizeof(pFunc->node.aliasName), "%s.%" PRId64 "", pFunc->functionName, pointer); } int32_t code = nodesListMakeStrictAppend(&pFunc->pParameterList, nodesCloneNode(pCol)); if (TSDB_CODE_SUCCESS == code) { code = fmGetFuncInfo(pFunc, NULL, 0); } if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pFunc); return NULL; } return (SNode*)pFunc; } static int32_t rewriteUniqueOptCreateAgg(SIndefRowsFuncLogicNode* pIndef, SLogicNode** pOutput) { SAggLogicNode* pAgg = (SAggLogicNode*)nodesMakeNode(QUERY_NODE_LOGIC_PLAN_AGG); if (NULL == pAgg) { return TSDB_CODE_OUT_OF_MEMORY; } TSWAP(pAgg->node.pChildren, pIndef->node.pChildren); optResetParent((SLogicNode*)pAgg); pAgg->node.precision = pIndef->node.precision; pAgg->node.requireDataOrder = DATA_ORDER_LEVEL_IN_BLOCK; // first function requirement pAgg->node.resultDataOrder = DATA_ORDER_LEVEL_NONE; int32_t code = TSDB_CODE_SUCCESS; bool hasSelectPrimaryKey = false; SNode* pPrimaryKey = NULL; SNode* pNode = NULL; FOREACH(pNode, pIndef->pFuncs) { SFunctionNode* pFunc = (SFunctionNode*)pNode; SNode* pExpr = nodesListGetNode(pFunc->pParameterList, 0); if (FUNCTION_TYPE_UNIQUE == pFunc->funcType) { pPrimaryKey = nodesListGetNode(pFunc->pParameterList, 1); code = nodesListMakeStrictAppend(&pAgg->pGroupKeys, rewriteUniqueOptCreateGroupingSet(pExpr)); } else if (PRIMARYKEY_TIMESTAMP_COL_ID == ((SColumnNode*)pExpr)->colId) { // _select_value(ts) => first(ts) hasSelectPrimaryKey = true; code = nodesListMakeStrictAppend(&pAgg->pAggFuncs, rewriteUniqueOptCreateFirstFunc(pFunc, pExpr)); } else { // _select_value(other_col) code = nodesListMakeStrictAppend(&pAgg->pAggFuncs, nodesCloneNode(pNode)); } if (TSDB_CODE_SUCCESS != code) { break; } } if (TSDB_CODE_SUCCESS == code) { code = createColumnByRewriteExprs(pAgg->pGroupKeys, &pAgg->node.pTargets); } if (TSDB_CODE_SUCCESS == code && NULL != pAgg->pAggFuncs) { code = createColumnByRewriteExprs(pAgg->pAggFuncs, &pAgg->node.pTargets); } if (TSDB_CODE_SUCCESS == code && !hasSelectPrimaryKey && NULL != pAgg->pAggFuncs) { code = nodesListMakeStrictAppend(&pAgg->pAggFuncs, rewriteUniqueOptCreateFirstFunc(NULL, pPrimaryKey)); } if (TSDB_CODE_SUCCESS == code) { *pOutput = (SLogicNode*)pAgg; } else { nodesDestroyNode((SNode*)pAgg); } return code; } static SNode* rewriteUniqueOptCreateProjectCol(SFunctionNode* pFunc) { SColumnNode* pCol = (SColumnNode*)nodesMakeNode(QUERY_NODE_COLUMN); if (NULL == pCol) { return NULL; } pCol->node.resType = pFunc->node.resType; if (FUNCTION_TYPE_UNIQUE == pFunc->funcType) { SExprNode* pExpr = (SExprNode*)nodesListGetNode(pFunc->pParameterList, 0); if (QUERY_NODE_COLUMN == nodeType(pExpr)) { strcpy(pCol->tableAlias, ((SColumnNode*)pExpr)->tableAlias); strcpy(pCol->colName, ((SColumnNode*)pExpr)->colName); } else { strcpy(pCol->colName, pExpr->aliasName); } } else { strcpy(pCol->colName, pFunc->node.aliasName); } strcpy(pCol->node.aliasName, pFunc->node.aliasName); return (SNode*)pCol; } static int32_t rewriteUniqueOptCreateProject(SIndefRowsFuncLogicNode* pIndef, SLogicNode** pOutput) { SProjectLogicNode* pProject = (SProjectLogicNode*)nodesMakeNode(QUERY_NODE_LOGIC_PLAN_PROJECT); if (NULL == pProject) { return TSDB_CODE_OUT_OF_MEMORY; } TSWAP(pProject->node.pTargets, pIndef->node.pTargets); pProject->node.precision = pIndef->node.precision; pProject->node.requireDataOrder = DATA_ORDER_LEVEL_NONE; pProject->node.resultDataOrder = DATA_ORDER_LEVEL_NONE; int32_t code = TSDB_CODE_SUCCESS; SNode* pNode = NULL; FOREACH(pNode, pIndef->pFuncs) { code = nodesListMakeStrictAppend(&pProject->pProjections, rewriteUniqueOptCreateProjectCol((SFunctionNode*)pNode)); if (TSDB_CODE_SUCCESS != code) { break; } } if (TSDB_CODE_SUCCESS == code) { *pOutput = (SLogicNode*)pProject; } else { nodesDestroyNode((SNode*)pProject); } return code; } static int32_t rewriteUniqueOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan, SIndefRowsFuncLogicNode* pIndef) { SLogicNode* pAgg = NULL; SLogicNode* pProject = NULL; int32_t code = rewriteUniqueOptCreateAgg(pIndef, &pAgg); if (TSDB_CODE_SUCCESS == code) { code = rewriteUniqueOptCreateProject(pIndef, &pProject); } if (TSDB_CODE_SUCCESS == code) { code = nodesListMakeAppend(&pProject->pChildren, (SNode*)pAgg); } if (TSDB_CODE_SUCCESS == code) { pAgg->pParent = pProject; pAgg = NULL; code = replaceLogicNode(pLogicSubplan, (SLogicNode*)pIndef, pProject); } if (TSDB_CODE_SUCCESS == code) { code = adjustLogicNodeDataRequirement( pProject, NULL == pProject->pParent ? DATA_ORDER_LEVEL_NONE : pProject->pParent->requireDataOrder); pProject = NULL; } if (TSDB_CODE_SUCCESS == code) { nodesDestroyNode((SNode*)pIndef); } else { nodesDestroyNode((SNode*)pAgg); nodesDestroyNode((SNode*)pProject); } pCxt->optimized = true; return code; } static int32_t rewriteUniqueOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) { SIndefRowsFuncLogicNode* pIndef = (SIndefRowsFuncLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, rewriteUniqueOptMayBeOptimized); if (NULL == pIndef) { return TSDB_CODE_SUCCESS; } return rewriteUniqueOptimizeImpl(pCxt, pLogicSubplan, pIndef); } typedef struct SLastRowScanOptLastParaCkCxt { bool hasTag; bool hasCol; } SLastRowScanOptLastParaCkCxt; static EDealRes lastRowScanOptLastParaCheckImpl(SNode* pNode, void* pContext) { if (QUERY_NODE_COLUMN == nodeType(pNode)) { SLastRowScanOptLastParaCkCxt* pCxt = pContext; if (COLUMN_TYPE_TAG == ((SColumnNode*)pNode)->colType || COLUMN_TYPE_TBNAME == ((SColumnNode*)pNode)->colType) { pCxt->hasTag = true; } else { pCxt->hasCol = true; } return DEAL_RES_END; } return DEAL_RES_CONTINUE; } static bool lastRowScanOptLastParaCheck(SNode* pExpr) { SLastRowScanOptLastParaCkCxt cxt = {.hasTag = false, .hasCol = false}; nodesWalkExpr(pExpr, lastRowScanOptLastParaCheckImpl, &cxt); return !cxt.hasTag && cxt.hasCol; } static bool hasSuitableCache(int8_t cacheLastMode, bool hasLastRow, bool hasLast) { switch (cacheLastMode) { case TSDB_CACHE_MODEL_NONE: return false; case TSDB_CACHE_MODEL_LAST_ROW: return hasLastRow; case TSDB_CACHE_MODEL_LAST_VALUE: return hasLast; case TSDB_CACHE_MODEL_BOTH: return true; default: break; } return false; } static bool lastRowScanOptMayBeOptimized(SLogicNode* pNode) { if (QUERY_NODE_LOGIC_PLAN_AGG != nodeType(pNode) || 1 != LIST_LENGTH(pNode->pChildren) || QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(nodesListGetNode(pNode->pChildren, 0))) { return false; } SAggLogicNode* pAgg = (SAggLogicNode*)pNode; SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(pNode->pChildren, 0); // Only one of LAST and LASTROW can appear if (pAgg->hasLastRow == pAgg->hasLast || NULL != pAgg->pGroupKeys || NULL != pScan->node.pConditions || !hasSuitableCache(pScan->cacheLastMode, pAgg->hasLastRow, pAgg->hasLast) || IS_TSWINDOW_SPECIFIED(pScan->scanRange)) { return false; } bool hasLastFunc = false; bool hasSelectFunc = false; SNode* pFunc = NULL; FOREACH(pFunc, ((SAggLogicNode*)pNode)->pAggFuncs) { SFunctionNode* pAggFunc = (SFunctionNode*)pFunc; if (FUNCTION_TYPE_LAST == pAggFunc->funcType) { if (hasSelectFunc || !lastRowScanOptLastParaCheck(nodesListGetNode(pAggFunc->pParameterList, 0))) { return false; } hasLastFunc = true; } else if (FUNCTION_TYPE_SELECT_VALUE == pAggFunc->funcType || FUNCTION_TYPE_GROUP_KEY == pAggFunc->funcType) { if (hasLastFunc) { return false; } hasSelectFunc = true; } else if (FUNCTION_TYPE_LAST_ROW != pAggFunc->funcType) { return false; } } return true; } typedef struct SLastRowScanOptSetColDataTypeCxt { bool doAgg; SNodeList* pLastCols; } SLastRowScanOptSetColDataTypeCxt; static EDealRes lastRowScanOptSetColDataType(SNode* pNode, void* pContext) { if (QUERY_NODE_COLUMN == nodeType(pNode)) { SLastRowScanOptSetColDataTypeCxt* pCxt = pContext; if (pCxt->doAgg) { nodesListMakeAppend(&pCxt->pLastCols, pNode); getLastCacheDataType(&(((SColumnNode*)pNode)->node.resType)); } else { SNode* pCol = NULL; FOREACH(pCol, pCxt->pLastCols) { if (nodesEqualNode(pCol, pNode)) { getLastCacheDataType(&(((SColumnNode*)pNode)->node.resType)); break; } } } return DEAL_RES_IGNORE_CHILD; } return DEAL_RES_CONTINUE; } static void lastRowScanOptSetLastTargets(SNodeList* pTargets, SNodeList* pLastCols) { SNode* pTarget = NULL; WHERE_EACH(pTarget, pTargets) { bool found = false; SNode* pCol = NULL; FOREACH(pCol, pLastCols) { if (nodesEqualNode(pCol, pTarget)) { getLastCacheDataType(&(((SColumnNode*)pTarget)->node.resType)); found = true; break; } } if (!found) { ERASE_NODE(pTargets); continue; } WHERE_NEXT; } } static int32_t lastRowScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) { SAggLogicNode* pAgg = (SAggLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, lastRowScanOptMayBeOptimized); if (NULL == pAgg) { return TSDB_CODE_SUCCESS; } SLastRowScanOptSetColDataTypeCxt cxt = {.doAgg = true, .pLastCols = NULL}; SNode* pNode = NULL; FOREACH(pNode, pAgg->pAggFuncs) { SFunctionNode* pFunc = (SFunctionNode*)pNode; int32_t funcType = pFunc->funcType; if (FUNCTION_TYPE_LAST_ROW == funcType || FUNCTION_TYPE_LAST == funcType) { int32_t len = snprintf(pFunc->functionName, sizeof(pFunc->functionName), FUNCTION_TYPE_LAST_ROW == funcType ? "_cache_last_row" : "_cache_last"); pFunc->functionName[len] = '\0'; int32_t code = fmGetFuncInfo(pFunc, NULL, 0); if (TSDB_CODE_SUCCESS != code) { nodesClearList(cxt.pLastCols); return code; } if (FUNCTION_TYPE_LAST == funcType) { nodesWalkExpr(nodesListGetNode(pFunc->pParameterList, 0), lastRowScanOptSetColDataType, &cxt); nodesListErase(pFunc->pParameterList, nodesListGetCell(pFunc->pParameterList, 1)); } } } SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(pAgg->node.pChildren, 0); pScan->scanType = SCAN_TYPE_LAST_ROW; pScan->igLastNull = pAgg->hasLast ? true : false; if (NULL != cxt.pLastCols) { cxt.doAgg = false; lastRowScanOptSetLastTargets(pScan->pScanCols, cxt.pLastCols); nodesWalkExprs(pScan->pScanPseudoCols, lastRowScanOptSetColDataType, &cxt); lastRowScanOptSetLastTargets(pScan->node.pTargets, cxt.pLastCols); nodesClearList(cxt.pLastCols); } pAgg->hasLastRow = false; pAgg->hasLast = false; pCxt->optimized = true; return TSDB_CODE_SUCCESS; } // merge projects static bool mergeProjectsMayBeOptimized(SLogicNode* pNode) { if (QUERY_NODE_LOGIC_PLAN_PROJECT != nodeType(pNode) || 1 != LIST_LENGTH(pNode->pChildren)) { return false; } SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pNode->pChildren, 0); if (QUERY_NODE_LOGIC_PLAN_PROJECT != nodeType(pChild) || 1 < LIST_LENGTH(pChild->pChildren) || NULL != pChild->pConditions || NULL != pChild->pLimit || NULL != pChild->pSlimit) { return false; } return true; } typedef struct SMergeProjectionsContext { SProjectLogicNode* pChildProj; int32_t errCode; } SMergeProjectionsContext; static EDealRes mergeProjectionsExpr(SNode** pNode, void* pContext) { SMergeProjectionsContext* pCxt = pContext; SProjectLogicNode* pChildProj = pCxt->pChildProj; if (QUERY_NODE_COLUMN == nodeType(*pNode)) { SNode* pTarget; FOREACH(pTarget, ((SLogicNode*)pChildProj)->pTargets) { if (nodesEqualNode(pTarget, *pNode)) { SNode* pProjection; FOREACH(pProjection, pChildProj->pProjections) { if (0 == strcmp(((SColumnNode*)pTarget)->colName, ((SExprNode*)pProjection)->aliasName)) { SNode* pExpr = nodesCloneNode(pProjection); if (pExpr == NULL) { pCxt->errCode = terrno; return DEAL_RES_ERROR; } snprintf(((SExprNode*)pExpr)->aliasName, sizeof(((SExprNode*)pExpr)->aliasName), "%s", ((SExprNode*)*pNode)->aliasName); nodesDestroyNode(*pNode); *pNode = pExpr; return DEAL_RES_IGNORE_CHILD; } } } } } return DEAL_RES_CONTINUE; } static int32_t mergeProjectsOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan, SLogicNode* pSelfNode) { SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pSelfNode->pChildren, 0); SMergeProjectionsContext cxt = {.pChildProj = (SProjectLogicNode*)pChild, .errCode = TSDB_CODE_SUCCESS}; nodesRewriteExprs(((SProjectLogicNode*)pSelfNode)->pProjections, mergeProjectionsExpr, &cxt); int32_t code = cxt.errCode; if (TSDB_CODE_SUCCESS == code) { if (1 == LIST_LENGTH(pChild->pChildren)) { SLogicNode* pGrandChild = (SLogicNode*)nodesListGetNode(pChild->pChildren, 0); code = replaceLogicNode(pLogicSubplan, pChild, pGrandChild); } else { // no grand child NODES_CLEAR_LIST(pSelfNode->pChildren); } } if (TSDB_CODE_SUCCESS == code) { NODES_CLEAR_LIST(pChild->pChildren); } nodesDestroyNode((SNode*)pChild); pCxt->optimized = true; return code; } static int32_t mergeProjectsOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) { SLogicNode* pProjectNode = optFindPossibleNode(pLogicSubplan->pNode, mergeProjectsMayBeOptimized); if (NULL == pProjectNode) { return TSDB_CODE_SUCCESS; } return mergeProjectsOptimizeImpl(pCxt, pLogicSubplan, pProjectNode); } static bool tagScanOptShouldBeOptimized(SLogicNode* pNode) { if (QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(pNode) || (SCAN_TYPE_TAG == ((SScanLogicNode*)pNode)->scanType)) { return false; } SScanLogicNode* pScan = (SScanLogicNode*)pNode; if (pScan->hasNormalCols) { return false; } if (NULL == pNode->pParent || QUERY_NODE_LOGIC_PLAN_AGG != nodeType(pNode->pParent) || 1 != LIST_LENGTH(pNode->pParent->pChildren)) { return false; } SAggLogicNode* pAgg = (SAggLogicNode*)(pNode->pParent); if (NULL == pAgg->pGroupKeys || NULL != pAgg->pAggFuncs || planOptNodeListHasCol(pAgg->pGroupKeys) || !planOptNodeListHasTbname(pAgg->pGroupKeys)) { return false; } SNode* pGroupKey = NULL; FOREACH(pGroupKey, pAgg->pGroupKeys) { SNode* pGroup = NULL; FOREACH(pGroup, ((SGroupingSetNode*)pGroupKey)->pParameterList) { if (QUERY_NODE_COLUMN != nodeType(pGroup)) { return false; } } } return true; } static int32_t tagScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) { SScanLogicNode* pScanNode = (SScanLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, tagScanOptShouldBeOptimized); if (NULL == pScanNode) { return TSDB_CODE_SUCCESS; } pScanNode->scanType = SCAN_TYPE_TAG; SNode* pTarget = NULL; FOREACH(pTarget, pScanNode->node.pTargets) { if (PRIMARYKEY_TIMESTAMP_COL_ID == ((SColumnNode*)(pTarget))->colId) { ERASE_NODE(pScanNode->node.pTargets); break; } } NODES_DESTORY_LIST(pScanNode->pScanCols); SLogicNode* pAgg = pScanNode->node.pParent; if (NULL == pAgg->pParent) { SNodeList* pScanTargets = nodesMakeList(); SNode* pAggTarget = NULL; FOREACH(pAggTarget, pAgg->pTargets) { SNode* pScanTarget = NULL; FOREACH(pScanTarget, pScanNode->node.pTargets) { if (0 == strcmp(((SColumnNode*)pAggTarget)->colName, ((SColumnNode*)pScanTarget)->colName)) { nodesListAppend(pScanTargets, nodesCloneNode(pScanTarget)); break; } } } nodesDestroyList(pScanNode->node.pTargets); pScanNode->node.pTargets = pScanTargets; } int32_t code = replaceLogicNode(pLogicSubplan, pAgg, (SLogicNode*)pScanNode); if (TSDB_CODE_SUCCESS == code) { NODES_CLEAR_LIST(pAgg->pChildren); } nodesDestroyNode((SNode*)pAgg); pCxt->optimized = true; return TSDB_CODE_SUCCESS; } static bool pushDownLimitOptShouldBeOptimized(SLogicNode* pNode) { if (NULL == pNode->pLimit || 1 != LIST_LENGTH(pNode->pChildren) || QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(nodesListGetNode(pNode->pChildren, 0))) { return false; } return true; } static int32_t pushDownLimitOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) { SLogicNode* pNode = optFindPossibleNode(pLogicSubplan->pNode, pushDownLimitOptShouldBeOptimized); if (NULL == pNode) { return TSDB_CODE_SUCCESS; } SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pNode->pChildren, 0); nodesDestroyNode(pChild->pLimit); pChild->pLimit = pNode->pLimit; pNode->pLimit = NULL; pCxt->optimized = true; return TSDB_CODE_SUCCESS; } // clang-format off static const SOptimizeRule optimizeRuleSet[] = { {.pName = "ScanPath", .optimizeFunc = scanPathOptimize}, {.pName = "PushDownCondition", .optimizeFunc = pushDownCondOptimize}, {.pName = "SortPrimaryKey", .optimizeFunc = sortPrimaryKeyOptimize}, {.pName = "SmaIndex", .optimizeFunc = smaIndexOptimize}, {.pName = "PartitionTags", .optimizeFunc = partTagsOptimize}, {.pName = "MergeProjects", .optimizeFunc = mergeProjectsOptimize}, {.pName = "EliminateProject", .optimizeFunc = eliminateProjOptimize}, {.pName = "EliminateSetOperator", .optimizeFunc = eliminateSetOpOptimize}, {.pName = "RewriteTail", .optimizeFunc = rewriteTailOptimize}, {.pName = "RewriteUnique", .optimizeFunc = rewriteUniqueOptimize}, {.pName = "LastRowScan", .optimizeFunc = lastRowScanOptimize}, {.pName = "TagScan", .optimizeFunc = tagScanOptimize}, {.pName = "PushDownLimit", .optimizeFunc = pushDownLimitOptimize} }; // clang-format on static const int32_t optimizeRuleNum = (sizeof(optimizeRuleSet) / sizeof(SOptimizeRule)); static void dumpLogicSubplan(const char* pRuleName, SLogicSubplan* pSubplan) { if (!tsQueryPlannerTrace) { return; } char* pStr = NULL; nodesNodeToString((SNode*)pSubplan, false, &pStr, NULL); if (NULL == pRuleName) { qDebugL("before optimize: %s", pStr); } else { qDebugL("apply optimize %s rule: %s", pRuleName, pStr); } taosMemoryFree(pStr); } static int32_t applyOptimizeRule(SPlanContext* pCxt, SLogicSubplan* pLogicSubplan) { SOptimizeContext cxt = {.pPlanCxt = pCxt, .optimized = false}; bool optimized = false; dumpLogicSubplan(NULL, pLogicSubplan); do { optimized = false; for (int32_t i = 0; i < optimizeRuleNum; ++i) { cxt.optimized = false; int32_t code = optimizeRuleSet[i].optimizeFunc(&cxt, pLogicSubplan); if (TSDB_CODE_SUCCESS != code) { return code; } if (cxt.optimized) { optimized = true; dumpLogicSubplan(optimizeRuleSet[i].pName, pLogicSubplan); } } } while (optimized); return TSDB_CODE_SUCCESS; } int32_t optimizeLogicPlan(SPlanContext* pCxt, SLogicSubplan* pLogicSubplan) { if (SUBPLAN_TYPE_MODIFY == pLogicSubplan->subplanType && NULL == pLogicSubplan->pNode->pChildren) { return TSDB_CODE_SUCCESS; } return applyOptimizeRule(pCxt, pLogicSubplan); }