Merge pull request #23622 from taosdata/feat/TD-24802
enh: optimize cache last and other functions performance
This commit is contained in:
commit
fd16b31b4e
|
@ -365,6 +365,11 @@ typedef struct SSortExecInfo {
|
|||
int32_t readBytes; // read io bytes
|
||||
} SSortExecInfo;
|
||||
|
||||
typedef struct SNonSortExecInfo {
|
||||
int32_t blkNums;
|
||||
} SNonSortExecInfo;
|
||||
|
||||
|
||||
typedef struct STUidTagInfo {
|
||||
char* name;
|
||||
uint64_t uid;
|
||||
|
|
|
@ -121,6 +121,7 @@ int32_t nodesListMakeAppend(SNodeList** pList, SNode* pNode);
|
|||
int32_t nodesListMakeStrictAppend(SNodeList** pList, SNode* pNode);
|
||||
int32_t nodesListAppendList(SNodeList* pTarget, SNodeList* pSrc);
|
||||
int32_t nodesListStrictAppendList(SNodeList* pTarget, SNodeList* pSrc);
|
||||
int32_t nodesListMakeStrictAppendList(SNodeList** pTarget, SNodeList* pSrc);
|
||||
int32_t nodesListPushFront(SNodeList* pList, SNode* pNode);
|
||||
SListCell* nodesListErase(SNodeList* pList, SListCell* pCell);
|
||||
void nodesListInsertList(SNodeList* pTarget, SListCell* pPos, SNodeList* pSrc);
|
||||
|
|
|
@ -40,6 +40,13 @@ typedef enum EGroupAction {
|
|||
GROUP_ACTION_CLEAR
|
||||
} EGroupAction;
|
||||
|
||||
typedef enum EMergeType {
|
||||
MERGE_TYPE_SORT = 1,
|
||||
MERGE_TYPE_NON_SORT,
|
||||
MERGE_TYPE_COLUMNS,
|
||||
MERGE_TYPE_MAX_VALUE
|
||||
} EMergeType;
|
||||
|
||||
typedef struct SLogicNode {
|
||||
ENodeType type;
|
||||
bool dynamicOp;
|
||||
|
@ -138,6 +145,7 @@ typedef struct SAggLogicNode {
|
|||
bool hasGroupKeyOptimized;
|
||||
bool isGroupTb;
|
||||
bool isPartTb; // true if partition keys has tbname
|
||||
bool hasGroup;
|
||||
} SAggLogicNode;
|
||||
|
||||
typedef struct SProjectLogicNode {
|
||||
|
@ -221,6 +229,8 @@ typedef struct SMergeLogicNode {
|
|||
SNodeList* pInputs;
|
||||
int32_t numOfChannels;
|
||||
int32_t srcGroupId;
|
||||
bool colsMerge;
|
||||
bool needSort;
|
||||
bool groupSort;
|
||||
bool ignoreGroupId;
|
||||
bool inputWithGroupId;
|
||||
|
@ -532,6 +542,7 @@ typedef struct SExchangePhysiNode {
|
|||
|
||||
typedef struct SMergePhysiNode {
|
||||
SPhysiNode node;
|
||||
EMergeType type;
|
||||
SNodeList* pMergeKeys;
|
||||
SNodeList* pTargets;
|
||||
int32_t numOfChannels;
|
||||
|
|
|
@ -37,7 +37,7 @@ extern "C" {
|
|||
#define EXPLAIN_TABLE_COUNT_SCAN_FORMAT "Table Count Row Scan on %s"
|
||||
#define EXPLAIN_PROJECTION_FORMAT "Projection"
|
||||
#define EXPLAIN_JOIN_FORMAT "%s"
|
||||
#define EXPLAIN_AGG_FORMAT "Aggragate"
|
||||
#define EXPLAIN_AGG_FORMAT "%s"
|
||||
#define EXPLAIN_INDEF_ROWS_FORMAT "Indefinite Rows Function"
|
||||
#define EXPLAIN_EXCHANGE_FORMAT "Data Exchange %d:1"
|
||||
#define EXPLAIN_SORT_FORMAT "Sort"
|
||||
|
@ -59,7 +59,7 @@ extern "C" {
|
|||
#define EXPLAIN_TIME_WINDOWS_FORMAT "Time Window: interval=%" PRId64 "%c offset=%" PRId64 "%c sliding=%" PRId64 "%c"
|
||||
#define EXPLAIN_WINDOW_FORMAT "Window: gap=%" PRId64
|
||||
#define EXPLAIN_RATIO_TIME_FORMAT "Ratio: %f"
|
||||
#define EXPLAIN_MERGE_FORMAT "SortMerge"
|
||||
#define EXPLAIN_MERGE_FORMAT "Merge"
|
||||
#define EXPLAIN_MERGE_KEYS_FORMAT "Merge Key: "
|
||||
#define EXPLAIN_IGNORE_GROUPID_FORMAT "Ignore Group Id: %s"
|
||||
#define EXPLAIN_PARTITION_KETS_FORMAT "Partition Key: "
|
||||
|
@ -85,7 +85,9 @@ extern "C" {
|
|||
#define EXPLAIN_COLUMNS_FORMAT "columns=%d"
|
||||
#define EXPLAIN_PSEUDO_COLUMNS_FORMAT "pseudo_columns=%d"
|
||||
#define EXPLAIN_WIDTH_FORMAT "width=%d"
|
||||
#define EXPLAIN_TABLE_SCAN_FORMAT "order=[asc|%d desc|%d]"
|
||||
#define EXPLAIN_SCAN_ORDER_FORMAT "order=[asc|%d desc|%d]"
|
||||
#define EXPLAIN_SCAN_MODE_FORMAT "mode=%s"
|
||||
#define EXPLAIN_SCAN_DATA_LOAD_FORMAT "data_load=%s"
|
||||
#define EXPLAIN_GROUPS_FORMAT "groups=%d"
|
||||
#define EXPLAIN_WIDTH_FORMAT "width=%d"
|
||||
#define EXPLAIN_INTERVAL_VALUE_FORMAT "interval=%" PRId64 "%c"
|
||||
|
@ -105,6 +107,7 @@ extern "C" {
|
|||
#define EXPLAIN_UID_SLOT_FORMAT "uid_slot=%d,%d"
|
||||
#define EXPLAIN_SRC_SCAN_FORMAT "src_scan=%d,%d"
|
||||
#define EXPLAIN_PLAN_BLOCKING "blocking=%d"
|
||||
#define EXPLAIN_MERGE_MODE_FORMAT "mode=%s"
|
||||
|
||||
#define COMMAND_RESET_LOG "resetLog"
|
||||
#define COMMAND_SCHEDULE_POLICY "schedulePolicy"
|
||||
|
@ -156,6 +159,7 @@ typedef struct SExplainCtx {
|
|||
|
||||
#define EXPLAIN_ORDER_STRING(_order) ((ORDER_ASC == _order) ? "asc" : ORDER_DESC == _order ? "desc" : "unknown")
|
||||
#define EXPLAIN_JOIN_STRING(_type) ((JOIN_TYPE_INNER == _type) ? "Inner join" : "Join")
|
||||
#define EXPLAIN_MERGE_MODE_STRING(_mode) ((_mode) == MERGE_TYPE_SORT ? "sort" : ((_mode) == MERGE_TYPE_NON_SORT ? "merge" : "column"))
|
||||
|
||||
#define INVERAL_TIME_FROM_PRECISION_TO_UNIT(_t, _u, _p) (((_u) == 'n' || (_u) == 'y') ? (_t) : (convertTimeFromPrecisionToUnit(_t, _p, _u)))
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "tcommon.h"
|
||||
#include "tdatablock.h"
|
||||
#include "systable.h"
|
||||
#include "functionMgt.h"
|
||||
|
||||
int32_t qExplainGenerateResNode(SPhysiNode *pNode, SExplainGroup *group, SExplainResNode **pRes);
|
||||
int32_t qExplainAppendGroupResRows(void *pCtx, int32_t groupId, int32_t level, bool singleChannel);
|
||||
|
@ -284,10 +285,49 @@ int32_t qExplainResAppendRow(SExplainCtx *ctx, char *tbuf, int32_t len, int32_t
|
|||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8_t getIntervalPrecision(SIntervalPhysiNode *pIntNode) {
|
||||
static uint8_t qExplainGetIntervalPrecision(SIntervalPhysiNode *pIntNode) {
|
||||
return ((SColumnNode *)pIntNode->window.pTspk)->node.resType.precision;
|
||||
}
|
||||
|
||||
static char* qExplainGetScanMode(STableScanPhysiNode* pScan) {
|
||||
bool isGroupByTbname = false;
|
||||
bool isGroupByTag = false;
|
||||
bool seq = false;
|
||||
bool groupOrder = false;
|
||||
if (pScan->pGroupTags && LIST_LENGTH(pScan->pGroupTags) == 1) {
|
||||
SNode* p = nodesListGetNode(pScan->pGroupTags, 0);
|
||||
if (QUERY_NODE_FUNCTION == nodeType(p) && (strcmp(((struct SFunctionNode*)p)->functionName, "tbname") == 0)) {
|
||||
isGroupByTbname = true;
|
||||
}
|
||||
}
|
||||
|
||||
isGroupByTag = (NULL != pScan->pGroupTags) && !isGroupByTbname;
|
||||
if ((((!isGroupByTag) || isGroupByTbname) && pScan->groupSort) || (isGroupByTag && (pScan->groupSort || pScan->scan.groupOrderScan))) {
|
||||
return "seq_grp_order";
|
||||
}
|
||||
|
||||
if ((isGroupByTbname && (pScan->groupSort || pScan->scan.groupOrderScan)) || (isGroupByTag && (pScan->groupSort || pScan->scan.groupOrderScan))) {
|
||||
return "grp_order";
|
||||
}
|
||||
|
||||
return "ts_order";
|
||||
}
|
||||
|
||||
static char* qExplainGetScanDataLoad(STableScanPhysiNode* pScan) {
|
||||
switch (pScan->dataRequired) {
|
||||
case FUNC_DATA_REQUIRED_DATA_LOAD:
|
||||
return "data";
|
||||
case FUNC_DATA_REQUIRED_SMA_LOAD:
|
||||
return "sma";
|
||||
case FUNC_DATA_REQUIRED_NOT_LOAD:
|
||||
return "no";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
int32_t qExplainResNodeToRowsImpl(SExplainResNode *pResNode, SExplainCtx *ctx, int32_t level) {
|
||||
int32_t tlen = 0;
|
||||
bool isVerboseLine = false;
|
||||
|
@ -360,7 +400,11 @@ int32_t qExplainResNodeToRowsImpl(SExplainResNode *pResNode, SExplainCtx *ctx, i
|
|||
}
|
||||
EXPLAIN_ROW_APPEND(EXPLAIN_WIDTH_FORMAT, pTblScanNode->scan.node.pOutputDataBlockDesc->totalRowSize);
|
||||
EXPLAIN_ROW_APPEND(EXPLAIN_BLANK_FORMAT);
|
||||
EXPLAIN_ROW_APPEND(EXPLAIN_TABLE_SCAN_FORMAT, pTblScanNode->scanSeq[0], pTblScanNode->scanSeq[1]);
|
||||
EXPLAIN_ROW_APPEND(EXPLAIN_SCAN_ORDER_FORMAT, pTblScanNode->scanSeq[0], pTblScanNode->scanSeq[1]);
|
||||
EXPLAIN_ROW_APPEND(EXPLAIN_BLANK_FORMAT);
|
||||
EXPLAIN_ROW_APPEND(EXPLAIN_SCAN_MODE_FORMAT, qExplainGetScanMode(pTblScanNode));
|
||||
EXPLAIN_ROW_APPEND(EXPLAIN_BLANK_FORMAT);
|
||||
EXPLAIN_ROW_APPEND(EXPLAIN_SCAN_DATA_LOAD_FORMAT, qExplainGetScanDataLoad(pTblScanNode));
|
||||
EXPLAIN_ROW_APPEND(EXPLAIN_RIGHT_PARENTHESIS_FORMAT);
|
||||
EXPLAIN_ROW_END();
|
||||
QRY_ERR_RET(qExplainResAppendRow(ctx, tbuf, tlen, level));
|
||||
|
@ -599,7 +643,7 @@ int32_t qExplainResNodeToRowsImpl(SExplainResNode *pResNode, SExplainCtx *ctx, i
|
|||
}
|
||||
case QUERY_NODE_PHYSICAL_PLAN_HASH_AGG: {
|
||||
SAggPhysiNode *pAggNode = (SAggPhysiNode *)pNode;
|
||||
EXPLAIN_ROW_NEW(level, EXPLAIN_AGG_FORMAT);
|
||||
EXPLAIN_ROW_NEW(level, EXPLAIN_AGG_FORMAT, (pAggNode->pGroupKeys ? "GroupAggragate" : "Aggragate"));
|
||||
EXPLAIN_ROW_APPEND(EXPLAIN_LEFT_PARENTHESIS_FORMAT);
|
||||
if (pResNode->pExecInfo) {
|
||||
QRY_ERR_RET(qExplainBufAppendExecInfo(pResNode->pExecInfo, tbuf, &tlen));
|
||||
|
@ -841,7 +885,7 @@ int32_t qExplainResNodeToRowsImpl(SExplainResNode *pResNode, SExplainCtx *ctx, i
|
|||
EXPLAIN_ROW_APPEND_SLIMIT(pIntNode->window.node.pSlimit);
|
||||
EXPLAIN_ROW_END();
|
||||
QRY_ERR_RET(qExplainResAppendRow(ctx, tbuf, tlen, level + 1));
|
||||
uint8_t precision = getIntervalPrecision(pIntNode);
|
||||
uint8_t precision = qExplainGetIntervalPrecision(pIntNode);
|
||||
EXPLAIN_ROW_NEW(level + 1, EXPLAIN_TIME_WINDOWS_FORMAT,
|
||||
INVERAL_TIME_FROM_PRECISION_TO_UNIT(pIntNode->interval, pIntNode->intervalUnit, precision),
|
||||
pIntNode->intervalUnit, pIntNode->offset, getPrecisionUnit(precision),
|
||||
|
@ -893,7 +937,7 @@ int32_t qExplainResNodeToRowsImpl(SExplainResNode *pResNode, SExplainCtx *ctx, i
|
|||
EXPLAIN_ROW_APPEND_SLIMIT(pIntNode->window.node.pSlimit);
|
||||
EXPLAIN_ROW_END();
|
||||
QRY_ERR_RET(qExplainResAppendRow(ctx, tbuf, tlen, level + 1));
|
||||
uint8_t precision = getIntervalPrecision(pIntNode);
|
||||
uint8_t precision = qExplainGetIntervalPrecision(pIntNode);
|
||||
EXPLAIN_ROW_NEW(level + 1, EXPLAIN_TIME_WINDOWS_FORMAT,
|
||||
INVERAL_TIME_FROM_PRECISION_TO_UNIT(pIntNode->interval, pIntNode->intervalUnit, precision),
|
||||
pIntNode->intervalUnit, pIntNode->offset, getPrecisionUnit(precision),
|
||||
|
@ -1119,23 +1163,14 @@ int32_t qExplainResNodeToRowsImpl(SExplainResNode *pResNode, SExplainCtx *ctx, i
|
|||
EXPLAIN_ROW_APPEND(EXPLAIN_INPUT_ORDER_FORMAT, EXPLAIN_ORDER_STRING(pMergeNode->node.inputTsOrder));
|
||||
EXPLAIN_ROW_APPEND(EXPLAIN_BLANK_FORMAT);
|
||||
EXPLAIN_ROW_APPEND(EXPLAIN_OUTPUT_ORDER_TYPE_FORMAT, EXPLAIN_ORDER_STRING(pMergeNode->node.outputTsOrder));
|
||||
EXPLAIN_ROW_APPEND(EXPLAIN_BLANK_FORMAT);
|
||||
EXPLAIN_ROW_APPEND(EXPLAIN_MERGE_MODE_FORMAT, EXPLAIN_MERGE_MODE_STRING(pMergeNode->type));
|
||||
EXPLAIN_ROW_APPEND(EXPLAIN_RIGHT_PARENTHESIS_FORMAT);
|
||||
EXPLAIN_ROW_END();
|
||||
QRY_ERR_RET(qExplainResAppendRow(ctx, tbuf, tlen, level));
|
||||
|
||||
if (EXPLAIN_MODE_ANALYZE == ctx->mode) {
|
||||
// sort key
|
||||
EXPLAIN_ROW_NEW(level + 1, "Merge Key: ");
|
||||
if (pResNode->pExecInfo) {
|
||||
for (int32_t i = 0; i < LIST_LENGTH(pMergeNode->pMergeKeys); ++i) {
|
||||
SOrderByExprNode *ptn = (SOrderByExprNode *)nodesListGetNode(pMergeNode->pMergeKeys, i);
|
||||
EXPLAIN_ROW_APPEND("%s ", nodesGetNameFromColumnNode(ptn->pExpr));
|
||||
}
|
||||
}
|
||||
|
||||
EXPLAIN_ROW_END();
|
||||
QRY_ERR_RET(qExplainResAppendRow(ctx, tbuf, tlen, level));
|
||||
|
||||
if (MERGE_TYPE_SORT == pMergeNode->type) {
|
||||
// sort method
|
||||
EXPLAIN_ROW_NEW(level + 1, "Sort Method: ");
|
||||
|
||||
|
@ -1155,6 +1190,7 @@ int32_t qExplainResNodeToRowsImpl(SExplainResNode *pResNode, SExplainCtx *ctx, i
|
|||
EXPLAIN_ROW_END();
|
||||
QRY_ERR_RET(qExplainResAppendRow(ctx, tbuf, tlen, level));
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
EXPLAIN_ROW_NEW(level + 1, EXPLAIN_OUTPUT_FORMAT);
|
||||
|
@ -1167,6 +1203,7 @@ int32_t qExplainResNodeToRowsImpl(SExplainResNode *pResNode, SExplainCtx *ctx, i
|
|||
EXPLAIN_ROW_END();
|
||||
QRY_ERR_RET(qExplainResAppendRow(ctx, tbuf, tlen, level + 1));
|
||||
|
||||
if (MERGE_TYPE_SORT == pMergeNode->type) {
|
||||
EXPLAIN_ROW_NEW(level + 1, EXPLAIN_OUTPUT_FORMAT);
|
||||
EXPLAIN_ROW_APPEND(EXPLAIN_IGNORE_GROUPID_FORMAT, pMergeNode->ignoreGroupId ? "true" : "false");
|
||||
EXPLAIN_ROW_END();
|
||||
|
@ -1190,6 +1227,7 @@ int32_t qExplainResNodeToRowsImpl(SExplainResNode *pResNode, SExplainCtx *ctx, i
|
|||
}
|
||||
EXPLAIN_ROW_END();
|
||||
QRY_ERR_RET(qExplainResAppendRow(ctx, tbuf, tlen, level + 1));
|
||||
}
|
||||
|
||||
if (pMergeNode->node.pConditions) {
|
||||
EXPLAIN_ROW_NEW(level + 1, EXPLAIN_FILTER_FORMAT);
|
||||
|
@ -1419,7 +1457,7 @@ int32_t qExplainResNodeToRowsImpl(SExplainResNode *pResNode, SExplainCtx *ctx, i
|
|||
EXPLAIN_ROW_APPEND_SLIMIT(pIntNode->window.node.pSlimit);
|
||||
EXPLAIN_ROW_END();
|
||||
QRY_ERR_RET(qExplainResAppendRow(ctx, tbuf, tlen, level + 1));
|
||||
uint8_t precision = getIntervalPrecision(pIntNode);
|
||||
uint8_t precision = qExplainGetIntervalPrecision(pIntNode);
|
||||
EXPLAIN_ROW_NEW(level + 1, EXPLAIN_TIME_WINDOWS_FORMAT,
|
||||
INVERAL_TIME_FROM_PRECISION_TO_UNIT(pIntNode->interval, pIntNode->intervalUnit, precision),
|
||||
pIntNode->intervalUnit, pIntNode->offset, getPrecisionUnit(precision),
|
||||
|
|
|
@ -239,7 +239,7 @@ SOperatorInfo* createMergeJoinOperatorInfo(SOperatorInfo** pDownstream, int32_t
|
|||
numOfDownstream = 2;
|
||||
} else {
|
||||
pInfo->downstreamResBlkId[0] = getOperatorResultBlockId(pDownstream[0], 0);
|
||||
pInfo->downstreamResBlkId[1] = getOperatorResultBlockId(pDownstream[1], 1);
|
||||
pInfo->downstreamResBlkId[1] = getOperatorResultBlockId(pDownstream[1], 0);
|
||||
}
|
||||
|
||||
int32_t numOfCols = 0;
|
||||
|
|
|
@ -0,0 +1,531 @@
|
|||
/*
|
||||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "executorInt.h"
|
||||
#include "filter.h"
|
||||
#include "operator.h"
|
||||
#include "querytask.h"
|
||||
#include "tdatablock.h"
|
||||
|
||||
typedef struct SSortMergeInfo {
|
||||
SArray* pSortInfo;
|
||||
SSortHandle* pSortHandle;
|
||||
STupleHandle* prefetchedTuple;
|
||||
int32_t bufPageSize;
|
||||
uint32_t sortBufSize; // max buffer size for in-memory sort
|
||||
SSDataBlock* pIntermediateBlock; // to hold the intermediate result
|
||||
SSDataBlock* pInputBlock;
|
||||
SColMatchInfo matchInfo;
|
||||
} SSortMergeInfo;
|
||||
|
||||
typedef struct SNonSortMergeInfo {
|
||||
int32_t lastSourceIdx;
|
||||
int32_t sourceWorkIdx;
|
||||
int32_t sourceNum;
|
||||
int32_t* pSourceStatus;
|
||||
} SNonSortMergeInfo;
|
||||
|
||||
typedef struct SColsMergeInfo {
|
||||
SNodeList* pTargets;
|
||||
uint64_t srcBlkIds[2];
|
||||
} SColsMergeInfo;
|
||||
|
||||
typedef struct SMultiwayMergeOperatorInfo {
|
||||
SOptrBasicInfo binfo;
|
||||
EMergeType type;
|
||||
union {
|
||||
SSortMergeInfo sortMergeInfo;
|
||||
SNonSortMergeInfo nsortMergeInfo;
|
||||
SColsMergeInfo colsMergeInfo;
|
||||
};
|
||||
SLimitInfo limitInfo;
|
||||
bool groupMerge;
|
||||
bool ignoreGroupId;
|
||||
uint64_t groupId;
|
||||
bool inputWithGroupId;
|
||||
} SMultiwayMergeOperatorInfo;
|
||||
|
||||
SSDataBlock* sortMergeloadNextDataBlock(void* param) {
|
||||
SOperatorInfo* pOperator = (SOperatorInfo*)param;
|
||||
SSDataBlock* pBlock = pOperator->fpSet.getNextFn(pOperator);
|
||||
return pBlock;
|
||||
}
|
||||
|
||||
int32_t openSortMergeOperator(SOperatorInfo* pOperator) {
|
||||
SMultiwayMergeOperatorInfo* pInfo = pOperator->info;
|
||||
SExecTaskInfo* pTaskInfo = pOperator->pTaskInfo;
|
||||
SSortMergeInfo* pSortMergeInfo = &pInfo->sortMergeInfo;
|
||||
|
||||
int32_t numOfBufPage = pSortMergeInfo->sortBufSize / pSortMergeInfo->bufPageSize;
|
||||
|
||||
pSortMergeInfo->pSortHandle = tsortCreateSortHandle(pSortMergeInfo->pSortInfo, SORT_MULTISOURCE_MERGE, pSortMergeInfo->bufPageSize, numOfBufPage,
|
||||
pSortMergeInfo->pInputBlock, pTaskInfo->id.str, 0, 0, 0);
|
||||
|
||||
tsortSetFetchRawDataFp(pSortMergeInfo->pSortHandle, sortMergeloadNextDataBlock, NULL, NULL);
|
||||
tsortSetCompareGroupId(pSortMergeInfo->pSortHandle, pInfo->groupMerge);
|
||||
|
||||
for (int32_t i = 0; i < pOperator->numOfDownstream; ++i) {
|
||||
SOperatorInfo* pDownstream = pOperator->pDownstream[i];
|
||||
if (pDownstream->operatorType == QUERY_NODE_PHYSICAL_PLAN_EXCHANGE) {
|
||||
pDownstream->fpSet._openFn(pDownstream);
|
||||
}
|
||||
|
||||
SSortSource* ps = taosMemoryCalloc(1, sizeof(SSortSource));
|
||||
ps->param = pDownstream;
|
||||
ps->onlyRef = true;
|
||||
|
||||
tsortAddSource(pSortMergeInfo->pSortHandle, ps);
|
||||
}
|
||||
|
||||
return tsortOpen(pSortMergeInfo->pSortHandle);
|
||||
}
|
||||
|
||||
static void doGetSortedBlockData(SMultiwayMergeOperatorInfo* pInfo, SSortHandle* pHandle, int32_t capacity,
|
||||
SSDataBlock* p, bool* newgroup) {
|
||||
SSortMergeInfo* pSortMergeInfo = &pInfo->sortMergeInfo;
|
||||
*newgroup = false;
|
||||
|
||||
while (1) {
|
||||
STupleHandle* pTupleHandle = NULL;
|
||||
if (pInfo->groupMerge || pInfo->inputWithGroupId) {
|
||||
if (pSortMergeInfo->prefetchedTuple == NULL) {
|
||||
pTupleHandle = tsortNextTuple(pHandle);
|
||||
} else {
|
||||
pTupleHandle = pSortMergeInfo->prefetchedTuple;
|
||||
pSortMergeInfo->prefetchedTuple = NULL;
|
||||
uint64_t gid = tsortGetGroupId(pTupleHandle);
|
||||
if (gid != pInfo->groupId) {
|
||||
*newgroup = true;
|
||||
pInfo->groupId = gid;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pTupleHandle = tsortNextTuple(pHandle);
|
||||
pInfo->groupId = 0;
|
||||
}
|
||||
|
||||
if (pTupleHandle == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (pInfo->groupMerge || pInfo->inputWithGroupId) {
|
||||
uint64_t tupleGroupId = tsortGetGroupId(pTupleHandle);
|
||||
if (pInfo->groupId == 0 || pInfo->groupId == tupleGroupId) {
|
||||
appendOneRowToDataBlock(p, pTupleHandle);
|
||||
p->info.id.groupId = tupleGroupId;
|
||||
pInfo->groupId = tupleGroupId;
|
||||
} else {
|
||||
if (p->info.rows == 0) {
|
||||
appendOneRowToDataBlock(p, pTupleHandle);
|
||||
p->info.id.groupId = pInfo->groupId = tupleGroupId;
|
||||
} else {
|
||||
pSortMergeInfo->prefetchedTuple = pTupleHandle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
appendOneRowToDataBlock(p, pTupleHandle);
|
||||
}
|
||||
|
||||
if (p->info.rows >= capacity) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SSDataBlock* doSortMerge(SOperatorInfo* pOperator) {
|
||||
SExecTaskInfo* pTaskInfo = pOperator->pTaskInfo;
|
||||
SMultiwayMergeOperatorInfo* pInfo = pOperator->info;
|
||||
SSortMergeInfo* pSortMergeInfo = &pInfo->sortMergeInfo;
|
||||
SSortHandle* pHandle = pSortMergeInfo->pSortHandle;
|
||||
SSDataBlock* pDataBlock = pInfo->binfo.pRes;
|
||||
SArray* pColMatchInfo = pSortMergeInfo->matchInfo.pList;
|
||||
int32_t capacity = pOperator->resultInfo.capacity;
|
||||
|
||||
qDebug("start to merge final sorted rows, %s", GET_TASKID(pTaskInfo));
|
||||
|
||||
blockDataCleanup(pDataBlock);
|
||||
|
||||
if (pSortMergeInfo->pIntermediateBlock == NULL) {
|
||||
pSortMergeInfo->pIntermediateBlock = tsortGetSortedDataBlock(pHandle);
|
||||
if (pSortMergeInfo->pIntermediateBlock == NULL) {
|
||||
terrno = TSDB_CODE_OUT_OF_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
blockDataEnsureCapacity(pSortMergeInfo->pIntermediateBlock, capacity);
|
||||
} else {
|
||||
blockDataCleanup(pSortMergeInfo->pIntermediateBlock);
|
||||
}
|
||||
|
||||
SSDataBlock* p = pSortMergeInfo->pIntermediateBlock;
|
||||
bool newgroup = false;
|
||||
|
||||
while (1) {
|
||||
doGetSortedBlockData(pInfo, pHandle, capacity, p, &newgroup);
|
||||
if (p->info.rows == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (newgroup) {
|
||||
resetLimitInfoForNextGroup(&pInfo->limitInfo);
|
||||
}
|
||||
|
||||
applyLimitOffset(&pInfo->limitInfo, p, pTaskInfo);
|
||||
|
||||
if (p->info.rows > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p->info.rows > 0) {
|
||||
int32_t numOfCols = taosArrayGetSize(pColMatchInfo);
|
||||
for (int32_t i = 0; i < numOfCols; ++i) {
|
||||
SColMatchItem* pmInfo = taosArrayGet(pColMatchInfo, i);
|
||||
|
||||
SColumnInfoData* pSrc = taosArrayGet(p->pDataBlock, pmInfo->srcSlotId);
|
||||
SColumnInfoData* pDst = taosArrayGet(pDataBlock->pDataBlock, pmInfo->dstSlotId);
|
||||
colDataAssign(pDst, pSrc, p->info.rows, &pDataBlock->info);
|
||||
}
|
||||
|
||||
pDataBlock->info.rows = p->info.rows;
|
||||
pDataBlock->info.scanFlag = p->info.scanFlag;
|
||||
if (pInfo->ignoreGroupId) {
|
||||
pDataBlock->info.id.groupId = 0;
|
||||
} else {
|
||||
pDataBlock->info.id.groupId = pInfo->groupId;
|
||||
}
|
||||
pDataBlock->info.dataLoad = 1;
|
||||
}
|
||||
|
||||
qDebug("%s get sorted block, groupId:0x%" PRIx64 " rows:%" PRId64 , GET_TASKID(pTaskInfo), pDataBlock->info.id.groupId,
|
||||
pDataBlock->info.rows);
|
||||
|
||||
return (pDataBlock->info.rows > 0) ? pDataBlock : NULL;
|
||||
}
|
||||
|
||||
|
||||
int32_t getSortMergeExplainExecInfo(SOperatorInfo* pOptr, void** pOptrExplain, uint32_t* len) {
|
||||
SSortExecInfo* pSortExecInfo = taosMemoryCalloc(1, sizeof(SSortExecInfo));
|
||||
|
||||
SMultiwayMergeOperatorInfo* pInfo = (SMultiwayMergeOperatorInfo*)pOptr->info;
|
||||
SSortMergeInfo* pSortMergeInfo = &pInfo->sortMergeInfo;
|
||||
|
||||
*pSortExecInfo = tsortGetSortExecInfo(pSortMergeInfo->pSortHandle);
|
||||
*pOptrExplain = pSortExecInfo;
|
||||
|
||||
*len = sizeof(SSortExecInfo);
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void destroySortMergeOperatorInfo(void* param) {
|
||||
SSortMergeInfo* pSortMergeInfo = param;
|
||||
pSortMergeInfo->pInputBlock = blockDataDestroy(pSortMergeInfo->pInputBlock);
|
||||
pSortMergeInfo->pIntermediateBlock = blockDataDestroy(pSortMergeInfo->pIntermediateBlock);
|
||||
|
||||
taosArrayDestroy(pSortMergeInfo->matchInfo.pList);
|
||||
|
||||
tsortDestroySortHandle(pSortMergeInfo->pSortHandle);
|
||||
taosArrayDestroy(pSortMergeInfo->pSortInfo);
|
||||
}
|
||||
|
||||
#define NON_SORT_NEXT_SRC(_info, _idx) ((++(_idx) >= (_info)->sourceNum) ? ((_info)->sourceWorkIdx) : (_idx))
|
||||
|
||||
int32_t openNonSortMergeOperator(SOperatorInfo* pOperator) {
|
||||
SMultiwayMergeOperatorInfo* pInfo = pOperator->info;
|
||||
SExecTaskInfo* pTaskInfo = pOperator->pTaskInfo;
|
||||
SNonSortMergeInfo* pNonSortMergeInfo = &pInfo->nsortMergeInfo;
|
||||
|
||||
pNonSortMergeInfo->sourceWorkIdx = 0;
|
||||
pNonSortMergeInfo->sourceNum = pOperator->numOfDownstream;
|
||||
pNonSortMergeInfo->lastSourceIdx = -1;
|
||||
pNonSortMergeInfo->pSourceStatus = taosMemoryCalloc(pOperator->numOfDownstream, sizeof(*pNonSortMergeInfo->pSourceStatus));
|
||||
if (NULL == pNonSortMergeInfo->pSourceStatus) {
|
||||
terrno = TSDB_CODE_OUT_OF_MEMORY;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < pOperator->numOfDownstream; ++i) {
|
||||
pNonSortMergeInfo->pSourceStatus[i] = i;
|
||||
}
|
||||
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
SSDataBlock* doNonSortMerge(SOperatorInfo* pOperator) {
|
||||
SExecTaskInfo* pTaskInfo = pOperator->pTaskInfo;
|
||||
SMultiwayMergeOperatorInfo* pInfo = pOperator->info;
|
||||
SNonSortMergeInfo* pNonSortMerge = &pInfo->nsortMergeInfo;
|
||||
SSDataBlock* pBlock = NULL;
|
||||
|
||||
qDebug("start to merge no sorted rows, %s", GET_TASKID(pTaskInfo));
|
||||
|
||||
int32_t idx = NON_SORT_NEXT_SRC(pNonSortMerge, pNonSortMerge->lastSourceIdx);
|
||||
while (idx < pNonSortMerge->sourceNum) {
|
||||
pBlock = getNextBlockFromDownstream(pOperator, pNonSortMerge->pSourceStatus[idx]);
|
||||
if (NULL == pBlock) {
|
||||
TSWAP(pNonSortMerge->pSourceStatus[pNonSortMerge->sourceWorkIdx], pNonSortMerge->pSourceStatus[idx]);
|
||||
pNonSortMerge->sourceWorkIdx++;
|
||||
idx = NON_SORT_NEXT_SRC(pNonSortMerge, idx);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return pBlock;
|
||||
}
|
||||
|
||||
void destroyNonSortMergeOperatorInfo(void* param) {
|
||||
SNonSortMergeInfo* pNonSortMerge = param;
|
||||
taosMemoryFree(pNonSortMerge->pSourceStatus);
|
||||
}
|
||||
|
||||
int32_t getNonSortMergeExplainExecInfo(SOperatorInfo* pOptr, void** pOptrExplain, uint32_t* len) {
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int32_t openColsMergeOperator(SOperatorInfo* pOperator) {
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t copyColumnsValue(SNodeList* pNodeList, uint64_t targetBlkId, SSDataBlock* pDst, SSDataBlock* pSrc) {
|
||||
bool isNull = (NULL == pSrc || pSrc->info.rows <= 0);
|
||||
size_t numOfCols = LIST_LENGTH(pNodeList);
|
||||
for (int32_t i = 0; i < numOfCols; ++i) {
|
||||
STargetNode* pNode = (STargetNode*)nodesListGetNode(pNodeList, i);
|
||||
if (nodeType(pNode->pExpr) == QUERY_NODE_COLUMN && ((SColumnNode*)pNode->pExpr)->dataBlockId == targetBlkId) {
|
||||
SColumnInfoData* pDstCol = taosArrayGet(pDst->pDataBlock, pNode->slotId);
|
||||
if (isNull) {
|
||||
colDataSetVal(pDstCol, 0, NULL, true);
|
||||
} else {
|
||||
SColumnInfoData* pSrcCol = taosArrayGet(pSrc->pDataBlock, ((SColumnNode*)pNode->pExpr)->slotId);
|
||||
colDataAssign(pDstCol, pSrcCol, 1, &pDst->info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
SSDataBlock* doColsMerge(SOperatorInfo* pOperator) {
|
||||
SExecTaskInfo* pTaskInfo = pOperator->pTaskInfo;
|
||||
SMultiwayMergeOperatorInfo* pInfo = pOperator->info;
|
||||
SSDataBlock* pBlock = NULL;
|
||||
SColsMergeInfo* pColsMerge = &pInfo->colsMergeInfo;
|
||||
int32_t nullBlkNum = 0;
|
||||
|
||||
qDebug("start to merge columns, %s", GET_TASKID(pTaskInfo));
|
||||
|
||||
for (int32_t i = 0; i < 2; ++i) {
|
||||
pBlock = getNextBlockFromDownstream(pOperator, i);
|
||||
if (pBlock && pBlock->info.rows > 1) {
|
||||
qError("more than 1 row returned from downstream, rows:%" PRId64, pBlock->info.rows);
|
||||
T_LONG_JMP(pTaskInfo->env, TSDB_CODE_QRY_EXECUTOR_INTERNAL_ERROR);
|
||||
} else if (NULL == pBlock) {
|
||||
nullBlkNum++;
|
||||
}
|
||||
|
||||
copyColumnsValue(pColsMerge->pTargets, pColsMerge->srcBlkIds[i], pInfo->binfo.pRes, pBlock);
|
||||
}
|
||||
|
||||
setOperatorCompleted(pOperator);
|
||||
|
||||
if (2 == nullBlkNum) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pInfo->binfo.pRes->info.rows = 1;
|
||||
|
||||
return pInfo->binfo.pRes;
|
||||
}
|
||||
|
||||
void destroyColsMergeOperatorInfo(void* param) {
|
||||
}
|
||||
|
||||
int32_t getColsMergeExplainExecInfo(SOperatorInfo* pOptr, void** pOptrExplain, uint32_t* len) {
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
SOperatorFpSet gMultiwayMergeFps[MERGE_TYPE_MAX_VALUE] = {
|
||||
{0},
|
||||
{._openFn = openSortMergeOperator, .getNextFn = doSortMerge, .closeFn = destroySortMergeOperatorInfo, .getExplainFn = getSortMergeExplainExecInfo},
|
||||
{._openFn = openNonSortMergeOperator, .getNextFn = doNonSortMerge, .closeFn = destroyNonSortMergeOperatorInfo, .getExplainFn = getNonSortMergeExplainExecInfo},
|
||||
{._openFn = openColsMergeOperator, .getNextFn = doColsMerge, .closeFn = destroyColsMergeOperatorInfo, .getExplainFn = getColsMergeExplainExecInfo},
|
||||
};
|
||||
|
||||
|
||||
int32_t openMultiwayMergeOperator(SOperatorInfo* pOperator) {
|
||||
int32_t code = 0;
|
||||
SMultiwayMergeOperatorInfo* pInfo = pOperator->info;
|
||||
SExecTaskInfo* pTaskInfo = pOperator->pTaskInfo;
|
||||
|
||||
if (OPTR_IS_OPENED(pOperator)) {
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
int64_t startTs = taosGetTimestampUs();
|
||||
|
||||
if (NULL != gMultiwayMergeFps[pInfo->type]._openFn) {
|
||||
code = (*gMultiwayMergeFps[pInfo->type]._openFn)(pOperator);
|
||||
}
|
||||
|
||||
pOperator->cost.openCost = (taosGetTimestampUs() - startTs) / 1000.0;
|
||||
pOperator->status = OP_RES_TO_RETURN;
|
||||
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
T_LONG_JMP(pTaskInfo->env, terrno);
|
||||
}
|
||||
|
||||
OPTR_SET_OPENED(pOperator);
|
||||
return code;
|
||||
}
|
||||
|
||||
SSDataBlock* doMultiwayMerge(SOperatorInfo* pOperator) {
|
||||
if (pOperator->status == OP_EXEC_DONE) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SSDataBlock* pBlock = NULL;
|
||||
SExecTaskInfo* pTaskInfo = pOperator->pTaskInfo;
|
||||
SMultiwayMergeOperatorInfo* pInfo = pOperator->info;
|
||||
|
||||
int32_t code = pOperator->fpSet._openFn(pOperator);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
T_LONG_JMP(pTaskInfo->env, code);
|
||||
}
|
||||
|
||||
if (NULL != gMultiwayMergeFps[pInfo->type].getNextFn) {
|
||||
pBlock = (*gMultiwayMergeFps[pInfo->type].getNextFn)(pOperator);
|
||||
}
|
||||
if (pBlock != NULL) {
|
||||
pOperator->resultInfo.totalRows += pBlock->info.rows;
|
||||
} else {
|
||||
setOperatorCompleted(pOperator);
|
||||
}
|
||||
|
||||
return pBlock;
|
||||
}
|
||||
|
||||
void destroyMultiwayMergeOperatorInfo(void* param) {
|
||||
SMultiwayMergeOperatorInfo* pInfo = (SMultiwayMergeOperatorInfo*)param;
|
||||
pInfo->binfo.pRes = blockDataDestroy(pInfo->binfo.pRes);
|
||||
|
||||
if (NULL != gMultiwayMergeFps[pInfo->type].closeFn) {
|
||||
(*gMultiwayMergeFps[pInfo->type].closeFn)(&pInfo->sortMergeInfo);
|
||||
}
|
||||
|
||||
taosMemoryFreeClear(param);
|
||||
}
|
||||
|
||||
int32_t getMultiwayMergeExplainExecInfo(SOperatorInfo* pOptr, void** pOptrExplain, uint32_t* len) {
|
||||
int32_t code = 0;
|
||||
SMultiwayMergeOperatorInfo* pInfo = (SMultiwayMergeOperatorInfo*)pOptr->info;
|
||||
|
||||
if (NULL != gMultiwayMergeFps[pInfo->type].getExplainFn) {
|
||||
code = (*gMultiwayMergeFps[pInfo->type].getExplainFn)(pOptr, pOptrExplain, len);
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
SOperatorInfo* createMultiwayMergeOperatorInfo(SOperatorInfo** downStreams, size_t numStreams,
|
||||
SMergePhysiNode* pMergePhyNode, SExecTaskInfo* pTaskInfo) {
|
||||
SPhysiNode* pPhyNode = (SPhysiNode*)pMergePhyNode;
|
||||
|
||||
SMultiwayMergeOperatorInfo* pInfo = taosMemoryCalloc(1, sizeof(SMultiwayMergeOperatorInfo));
|
||||
SOperatorInfo* pOperator = taosMemoryCalloc(1, sizeof(SOperatorInfo));
|
||||
SDataBlockDescNode* pDescNode = pPhyNode->pOutputDataBlockDesc;
|
||||
|
||||
int32_t code = TSDB_CODE_SUCCESS;
|
||||
if (pInfo == NULL || pOperator == NULL) {
|
||||
code = TSDB_CODE_OUT_OF_MEMORY;
|
||||
goto _error;
|
||||
}
|
||||
|
||||
pInfo->groupMerge = pMergePhyNode->groupSort;
|
||||
pInfo->ignoreGroupId = pMergePhyNode->ignoreGroupId;
|
||||
pInfo->binfo.inputTsOrder = pMergePhyNode->node.inputTsOrder;
|
||||
pInfo->binfo.outputTsOrder = pMergePhyNode->node.outputTsOrder;
|
||||
pInfo->inputWithGroupId = pMergePhyNode->inputWithGroupId;
|
||||
|
||||
pInfo->type = pMergePhyNode->type;
|
||||
switch (pInfo->type) {
|
||||
case MERGE_TYPE_SORT: {
|
||||
SSortMergeInfo* pSortMergeInfo = &pInfo->sortMergeInfo;
|
||||
initLimitInfo(pMergePhyNode->node.pLimit, pMergePhyNode->node.pSlimit, &pInfo->limitInfo);
|
||||
pInfo->binfo.pRes = createDataBlockFromDescNode(pDescNode);
|
||||
|
||||
SPhysiNode* pChildNode = (SPhysiNode*)nodesListGetNode(pPhyNode->pChildren, 0);
|
||||
SSDataBlock* pInputBlock = createDataBlockFromDescNode(pChildNode->pOutputDataBlockDesc);
|
||||
|
||||
initResultSizeInfo(&pOperator->resultInfo, 1024);
|
||||
blockDataEnsureCapacity(pInfo->binfo.pRes, pOperator->resultInfo.capacity);
|
||||
|
||||
size_t numOfCols = taosArrayGetSize(pInfo->binfo.pRes->pDataBlock);
|
||||
int32_t rowSize = pInfo->binfo.pRes->info.rowSize;
|
||||
int32_t numOfOutputCols = 0;
|
||||
pSortMergeInfo->pSortInfo = createSortInfo(pMergePhyNode->pMergeKeys);
|
||||
pSortMergeInfo->bufPageSize = getProperSortPageSize(rowSize, numOfCols);
|
||||
pSortMergeInfo->sortBufSize = pSortMergeInfo->bufPageSize * (numStreams + 1); // one additional is reserved for merged result.
|
||||
pSortMergeInfo->pInputBlock = pInputBlock;
|
||||
code = extractColMatchInfo(pMergePhyNode->pTargets, pDescNode, &numOfOutputCols, COL_MATCH_FROM_SLOT_ID,
|
||||
&pSortMergeInfo->matchInfo);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
goto _error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MERGE_TYPE_NON_SORT: {
|
||||
SNonSortMergeInfo* pNonSortMerge = &pInfo->nsortMergeInfo;
|
||||
break;
|
||||
}
|
||||
case MERGE_TYPE_COLUMNS: {
|
||||
SColsMergeInfo* pColsMerge = &pInfo->colsMergeInfo;
|
||||
pInfo->binfo.pRes = createDataBlockFromDescNode(pDescNode);
|
||||
initResultSizeInfo(&pOperator->resultInfo, 1);
|
||||
blockDataEnsureCapacity(pInfo->binfo.pRes, pOperator->resultInfo.capacity);
|
||||
|
||||
pColsMerge->pTargets = pMergePhyNode->pTargets;
|
||||
pColsMerge->srcBlkIds[0] = getOperatorResultBlockId(downStreams[0], 0);
|
||||
pColsMerge->srcBlkIds[1] = getOperatorResultBlockId(downStreams[1], 0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
qError("Invalid merge type: %d", pInfo->type);
|
||||
code = TSDB_CODE_INVALID_PARA;
|
||||
goto _error;
|
||||
}
|
||||
|
||||
setOperatorInfo(pOperator, "MultiwayMergeOperator", QUERY_NODE_PHYSICAL_PLAN_MERGE, false, OP_NOT_OPENED, pInfo, pTaskInfo);
|
||||
pOperator->fpSet = createOperatorFpSet(openMultiwayMergeOperator, doMultiwayMerge, NULL,
|
||||
destroyMultiwayMergeOperatorInfo, optrDefaultBufFn, getMultiwayMergeExplainExecInfo, optrDefaultGetNextExtFn, NULL);
|
||||
|
||||
code = appendDownstream(pOperator, downStreams, numStreams);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
goto _error;
|
||||
}
|
||||
return pOperator;
|
||||
|
||||
_error:
|
||||
if (pInfo != NULL) {
|
||||
destroyMultiwayMergeOperatorInfo(pInfo);
|
||||
}
|
||||
|
||||
pTaskInfo->code = code;
|
||||
taosMemoryFree(pOperator);
|
||||
return NULL;
|
||||
}
|
|
@ -675,293 +675,5 @@ _error:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
//=====================================================================================
|
||||
// Multiway Sort Merge operator
|
||||
typedef struct SMultiwayMergeOperatorInfo {
|
||||
SOptrBasicInfo binfo;
|
||||
int32_t bufPageSize;
|
||||
uint32_t sortBufSize; // max buffer size for in-memory sort
|
||||
SLimitInfo limitInfo;
|
||||
SArray* pSortInfo;
|
||||
SSortHandle* pSortHandle;
|
||||
SColMatchInfo matchInfo;
|
||||
SSDataBlock* pInputBlock;
|
||||
SSDataBlock* pIntermediateBlock; // to hold the intermediate result
|
||||
int64_t startTs; // sort start time
|
||||
bool groupSort;
|
||||
bool ignoreGroupId;
|
||||
uint64_t groupId;
|
||||
STupleHandle* prefetchedTuple;
|
||||
bool inputWithGroupId;
|
||||
} SMultiwayMergeOperatorInfo;
|
||||
|
||||
int32_t openMultiwayMergeOperator(SOperatorInfo* pOperator) {
|
||||
SMultiwayMergeOperatorInfo* pInfo = pOperator->info;
|
||||
SExecTaskInfo* pTaskInfo = pOperator->pTaskInfo;
|
||||
|
||||
if (OPTR_IS_OPENED(pOperator)) {
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
pInfo->startTs = taosGetTimestampUs();
|
||||
int32_t numOfBufPage = pInfo->sortBufSize / pInfo->bufPageSize;
|
||||
|
||||
pInfo->pSortHandle = tsortCreateSortHandle(pInfo->pSortInfo, SORT_MULTISOURCE_MERGE, pInfo->bufPageSize, numOfBufPage,
|
||||
pInfo->pInputBlock, pTaskInfo->id.str, 0, 0, 0);
|
||||
|
||||
tsortSetFetchRawDataFp(pInfo->pSortHandle, loadNextDataBlock, NULL, NULL);
|
||||
tsortSetCompareGroupId(pInfo->pSortHandle, pInfo->groupSort);
|
||||
|
||||
for (int32_t i = 0; i < pOperator->numOfDownstream; ++i) {
|
||||
SOperatorInfo* pDownstream = pOperator->pDownstream[i];
|
||||
if (pDownstream->operatorType == QUERY_NODE_PHYSICAL_PLAN_EXCHANGE) {
|
||||
pDownstream->fpSet._openFn(pDownstream);
|
||||
}
|
||||
|
||||
SSortSource* ps = taosMemoryCalloc(1, sizeof(SSortSource));
|
||||
ps->param = pDownstream;
|
||||
ps->onlyRef = true;
|
||||
|
||||
tsortAddSource(pInfo->pSortHandle, ps);
|
||||
}
|
||||
|
||||
int32_t code = tsortOpen(pInfo->pSortHandle);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
T_LONG_JMP(pTaskInfo->env, terrno);
|
||||
}
|
||||
|
||||
pOperator->cost.openCost = (taosGetTimestampUs() - pInfo->startTs) / 1000.0;
|
||||
pOperator->status = OP_RES_TO_RETURN;
|
||||
|
||||
OPTR_SET_OPENED(pOperator);
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
static void doGetSortedBlockData(SMultiwayMergeOperatorInfo* pInfo, SSortHandle* pHandle, int32_t capacity,
|
||||
SSDataBlock* p, bool* newgroup) {
|
||||
*newgroup = false;
|
||||
|
||||
while (1) {
|
||||
STupleHandle* pTupleHandle = NULL;
|
||||
if (pInfo->groupSort || pInfo->inputWithGroupId) {
|
||||
if (pInfo->prefetchedTuple == NULL) {
|
||||
pTupleHandle = tsortNextTuple(pHandle);
|
||||
} else {
|
||||
pTupleHandle = pInfo->prefetchedTuple;
|
||||
pInfo->prefetchedTuple = NULL;
|
||||
uint64_t gid = tsortGetGroupId(pTupleHandle);
|
||||
if (gid != pInfo->groupId) {
|
||||
*newgroup = true;
|
||||
pInfo->groupId = gid;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pTupleHandle = tsortNextTuple(pHandle);
|
||||
pInfo->groupId = 0;
|
||||
}
|
||||
|
||||
if (pTupleHandle == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (pInfo->groupSort || pInfo->inputWithGroupId) {
|
||||
uint64_t tupleGroupId = tsortGetGroupId(pTupleHandle);
|
||||
if (pInfo->groupId == 0 || pInfo->groupId == tupleGroupId) {
|
||||
appendOneRowToDataBlock(p, pTupleHandle);
|
||||
p->info.id.groupId = tupleGroupId;
|
||||
pInfo->groupId = tupleGroupId;
|
||||
} else {
|
||||
if (p->info.rows == 0) {
|
||||
appendOneRowToDataBlock(p, pTupleHandle);
|
||||
p->info.id.groupId = pInfo->groupId = tupleGroupId;
|
||||
} else {
|
||||
pInfo->prefetchedTuple = pTupleHandle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
appendOneRowToDataBlock(p, pTupleHandle);
|
||||
}
|
||||
|
||||
if (p->info.rows >= capacity) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SSDataBlock* getMultiwaySortedBlockData(SSortHandle* pHandle, SSDataBlock* pDataBlock, SArray* pColMatchInfo,
|
||||
SOperatorInfo* pOperator) {
|
||||
SMultiwayMergeOperatorInfo* pInfo = pOperator->info;
|
||||
|
||||
int32_t capacity = pOperator->resultInfo.capacity;
|
||||
|
||||
SExecTaskInfo* pTaskInfo = pOperator->pTaskInfo;
|
||||
blockDataCleanup(pDataBlock);
|
||||
|
||||
if (pInfo->pIntermediateBlock == NULL) {
|
||||
pInfo->pIntermediateBlock = tsortGetSortedDataBlock(pHandle);
|
||||
if (pInfo->pIntermediateBlock == NULL) {
|
||||
terrno = TSDB_CODE_OUT_OF_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
blockDataEnsureCapacity(pInfo->pIntermediateBlock, capacity);
|
||||
} else {
|
||||
blockDataCleanup(pInfo->pIntermediateBlock);
|
||||
}
|
||||
|
||||
SSDataBlock* p = pInfo->pIntermediateBlock;
|
||||
bool newgroup = false;
|
||||
|
||||
while (1) {
|
||||
doGetSortedBlockData(pInfo, pHandle, capacity, p, &newgroup);
|
||||
if (p->info.rows == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (newgroup) {
|
||||
resetLimitInfoForNextGroup(&pInfo->limitInfo);
|
||||
}
|
||||
|
||||
applyLimitOffset(&pInfo->limitInfo, p, pTaskInfo);
|
||||
|
||||
if (p->info.rows > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p->info.rows > 0) {
|
||||
int32_t numOfCols = taosArrayGetSize(pColMatchInfo);
|
||||
for (int32_t i = 0; i < numOfCols; ++i) {
|
||||
SColMatchItem* pmInfo = taosArrayGet(pColMatchInfo, i);
|
||||
|
||||
SColumnInfoData* pSrc = taosArrayGet(p->pDataBlock, pmInfo->srcSlotId);
|
||||
SColumnInfoData* pDst = taosArrayGet(pDataBlock->pDataBlock, pmInfo->dstSlotId);
|
||||
colDataAssign(pDst, pSrc, p->info.rows, &pDataBlock->info);
|
||||
}
|
||||
|
||||
pDataBlock->info.rows = p->info.rows;
|
||||
pDataBlock->info.scanFlag = p->info.scanFlag;
|
||||
if (pInfo->ignoreGroupId) {
|
||||
pDataBlock->info.id.groupId = 0;
|
||||
} else {
|
||||
pDataBlock->info.id.groupId = pInfo->groupId;
|
||||
}
|
||||
pDataBlock->info.dataLoad = 1;
|
||||
}
|
||||
|
||||
qDebug("%s get sorted block, groupId:0x%" PRIx64 " rows:%" PRId64 , GET_TASKID(pTaskInfo), pDataBlock->info.id.groupId,
|
||||
pDataBlock->info.rows);
|
||||
|
||||
return (pDataBlock->info.rows > 0) ? pDataBlock : NULL;
|
||||
}
|
||||
|
||||
SSDataBlock* doMultiwayMerge(SOperatorInfo* pOperator) {
|
||||
if (pOperator->status == OP_EXEC_DONE) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SExecTaskInfo* pTaskInfo = pOperator->pTaskInfo;
|
||||
SMultiwayMergeOperatorInfo* pInfo = pOperator->info;
|
||||
|
||||
int32_t code = pOperator->fpSet._openFn(pOperator);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
T_LONG_JMP(pTaskInfo->env, code);
|
||||
}
|
||||
|
||||
qDebug("start to merge final sorted rows, %s", GET_TASKID(pTaskInfo));
|
||||
SSDataBlock* pBlock = getMultiwaySortedBlockData(pInfo->pSortHandle, pInfo->binfo.pRes, pInfo->matchInfo.pList, pOperator);
|
||||
if (pBlock != NULL) {
|
||||
pOperator->resultInfo.totalRows += pBlock->info.rows;
|
||||
} else {
|
||||
setOperatorCompleted(pOperator);
|
||||
}
|
||||
|
||||
return pBlock;
|
||||
}
|
||||
|
||||
void destroyMultiwayMergeOperatorInfo(void* param) {
|
||||
SMultiwayMergeOperatorInfo* pInfo = (SMultiwayMergeOperatorInfo*)param;
|
||||
pInfo->binfo.pRes = blockDataDestroy(pInfo->binfo.pRes);
|
||||
pInfo->pInputBlock = blockDataDestroy(pInfo->pInputBlock);
|
||||
pInfo->pIntermediateBlock = blockDataDestroy(pInfo->pIntermediateBlock);
|
||||
|
||||
tsortDestroySortHandle(pInfo->pSortHandle);
|
||||
taosArrayDestroy(pInfo->pSortInfo);
|
||||
taosArrayDestroy(pInfo->matchInfo.pList);
|
||||
|
||||
taosMemoryFreeClear(param);
|
||||
}
|
||||
|
||||
int32_t getMultiwayMergeExplainExecInfo(SOperatorInfo* pOptr, void** pOptrExplain, uint32_t* len) {
|
||||
SSortExecInfo* pSortExecInfo = taosMemoryCalloc(1, sizeof(SSortExecInfo));
|
||||
|
||||
SMultiwayMergeOperatorInfo* pInfo = (SMultiwayMergeOperatorInfo*)pOptr->info;
|
||||
|
||||
*pSortExecInfo = tsortGetSortExecInfo(pInfo->pSortHandle);
|
||||
*pOptrExplain = pSortExecInfo;
|
||||
|
||||
*len = sizeof(SSortExecInfo);
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
SOperatorInfo* createMultiwayMergeOperatorInfo(SOperatorInfo** downStreams, size_t numStreams,
|
||||
SMergePhysiNode* pMergePhyNode, SExecTaskInfo* pTaskInfo) {
|
||||
SPhysiNode* pPhyNode = (SPhysiNode*)pMergePhyNode;
|
||||
|
||||
SMultiwayMergeOperatorInfo* pInfo = taosMemoryCalloc(1, sizeof(SMultiwayMergeOperatorInfo));
|
||||
SOperatorInfo* pOperator = taosMemoryCalloc(1, sizeof(SOperatorInfo));
|
||||
SDataBlockDescNode* pDescNode = pPhyNode->pOutputDataBlockDesc;
|
||||
|
||||
int32_t code = TSDB_CODE_SUCCESS;
|
||||
if (pInfo == NULL || pOperator == NULL) {
|
||||
code = TSDB_CODE_OUT_OF_MEMORY;
|
||||
goto _error;
|
||||
}
|
||||
|
||||
initLimitInfo(pMergePhyNode->node.pLimit, pMergePhyNode->node.pSlimit, &pInfo->limitInfo);
|
||||
pInfo->binfo.pRes = createDataBlockFromDescNode(pDescNode);
|
||||
|
||||
int32_t rowSize = pInfo->binfo.pRes->info.rowSize;
|
||||
int32_t numOfOutputCols = 0;
|
||||
code = extractColMatchInfo(pMergePhyNode->pTargets, pDescNode, &numOfOutputCols, COL_MATCH_FROM_SLOT_ID,
|
||||
&pInfo->matchInfo);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
goto _error;
|
||||
}
|
||||
|
||||
SPhysiNode* pChildNode = (SPhysiNode*)nodesListGetNode(pPhyNode->pChildren, 0);
|
||||
SSDataBlock* pInputBlock = createDataBlockFromDescNode(pChildNode->pOutputDataBlockDesc);
|
||||
|
||||
initResultSizeInfo(&pOperator->resultInfo, 1024);
|
||||
blockDataEnsureCapacity(pInfo->binfo.pRes, pOperator->resultInfo.capacity);
|
||||
|
||||
pInfo->groupSort = pMergePhyNode->groupSort;
|
||||
pInfo->ignoreGroupId = pMergePhyNode->ignoreGroupId;
|
||||
pInfo->pSortInfo = createSortInfo(pMergePhyNode->pMergeKeys);
|
||||
pInfo->pInputBlock = pInputBlock;
|
||||
size_t numOfCols = taosArrayGetSize(pInfo->binfo.pRes->pDataBlock);
|
||||
pInfo->bufPageSize = getProperSortPageSize(rowSize, numOfCols);
|
||||
pInfo->sortBufSize = pInfo->bufPageSize * (numStreams + 1); // one additional is reserved for merged result.
|
||||
pInfo->binfo.inputTsOrder = pMergePhyNode->node.inputTsOrder;
|
||||
pInfo->binfo.outputTsOrder = pMergePhyNode->node.outputTsOrder;
|
||||
pInfo->inputWithGroupId = pMergePhyNode->inputWithGroupId;
|
||||
|
||||
setOperatorInfo(pOperator, "MultiwayMergeOperator", QUERY_NODE_PHYSICAL_PLAN_MERGE, false, OP_NOT_OPENED, pInfo, pTaskInfo);
|
||||
pOperator->fpSet = createOperatorFpSet(openMultiwayMergeOperator, doMultiwayMerge, NULL,
|
||||
destroyMultiwayMergeOperatorInfo, optrDefaultBufFn, getMultiwayMergeExplainExecInfo, optrDefaultGetNextExtFn, NULL);
|
||||
|
||||
code = appendDownstream(pOperator, downStreams, numStreams);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
goto _error;
|
||||
}
|
||||
return pOperator;
|
||||
|
||||
_error:
|
||||
if (pInfo != NULL) {
|
||||
destroyMultiwayMergeOperatorInfo(pInfo);
|
||||
}
|
||||
|
||||
pTaskInfo->code = code;
|
||||
taosMemoryFree(pOperator);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -419,6 +419,7 @@ static int32_t logicScanCopy(const SScanLogicNode* pSrc, SScanLogicNode* pDst) {
|
|||
COPY_SCALAR_FIELD(groupSort);
|
||||
CLONE_NODE_LIST_FIELD(pTags);
|
||||
CLONE_NODE_FIELD(pSubtable);
|
||||
COPY_SCALAR_FIELD(cacheLastMode);
|
||||
COPY_SCALAR_FIELD(igLastNull);
|
||||
COPY_SCALAR_FIELD(groupOrderScan);
|
||||
COPY_SCALAR_FIELD(onlyMetaCtbIdx);
|
||||
|
@ -443,8 +444,14 @@ static int32_t logicAggCopy(const SAggLogicNode* pSrc, SAggLogicNode* pDst) {
|
|||
COPY_BASE_OBJECT_FIELD(node, logicNodeCopy);
|
||||
CLONE_NODE_LIST_FIELD(pGroupKeys);
|
||||
CLONE_NODE_LIST_FIELD(pAggFuncs);
|
||||
COPY_SCALAR_FIELD(hasLastRow);
|
||||
COPY_SCALAR_FIELD(hasLast);
|
||||
COPY_SCALAR_FIELD(hasTimeLineFunc);
|
||||
COPY_SCALAR_FIELD(onlyHasKeepOrderFunc);
|
||||
COPY_SCALAR_FIELD(hasGroupKeyOptimized);
|
||||
COPY_SCALAR_FIELD(isGroupTb);
|
||||
COPY_SCALAR_FIELD(isPartTb);
|
||||
COPY_SCALAR_FIELD(hasGroup);
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -488,6 +495,8 @@ static int32_t logicMergeCopy(const SMergeLogicNode* pSrc, SMergeLogicNode* pDst
|
|||
CLONE_NODE_LIST_FIELD(pInputs);
|
||||
COPY_SCALAR_FIELD(numOfChannels);
|
||||
COPY_SCALAR_FIELD(srcGroupId);
|
||||
COPY_SCALAR_FIELD(colsMerge);
|
||||
COPY_SCALAR_FIELD(needSort);
|
||||
COPY_SCALAR_FIELD(groupSort);
|
||||
COPY_SCALAR_FIELD(ignoreGroupId);
|
||||
COPY_SCALAR_FIELD(inputWithGroupId);
|
||||
|
|
|
@ -2285,6 +2285,7 @@ static const char* jkMergePhysiPlanSrcGroupId = "SrcGroupId";
|
|||
static const char* jkMergePhysiPlanGroupSort = "GroupSort";
|
||||
static const char* jkMergePhysiPlanIgnoreGroupID = "IgnoreGroupID";
|
||||
static const char* jkMergePhysiPlanInputWithGroupId = "InputWithGroupId";
|
||||
static const char* jkMergePhysiPlanType = "Type";
|
||||
|
||||
static int32_t physiMergeNodeToJson(const void* pObj, SJson* pJson) {
|
||||
const SMergePhysiNode* pNode = (const SMergePhysiNode*)pObj;
|
||||
|
@ -2311,6 +2312,9 @@ static int32_t physiMergeNodeToJson(const void* pObj, SJson* pJson) {
|
|||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = tjsonAddBoolToObject(pJson, jkMergePhysiPlanInputWithGroupId, pNode->inputWithGroupId);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = tjsonAddIntegerToObject(pJson, jkMergePhysiPlanType, pNode->type);
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
@ -2337,6 +2341,9 @@ static int32_t jsonToPhysiMergeNode(const SJson* pJson, void* pObj) {
|
|||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = tjsonGetBoolValue(pJson, jkMergePhysiPlanIgnoreGroupID, &pNode->ignoreGroupId);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = tjsonGetIntValue(pJson, jkMergePhysiPlanType, (int32_t*)&pNode->type);
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
|
|
@ -2690,6 +2690,7 @@ enum {
|
|||
PHY_MERGE_CODE_GROUP_SORT,
|
||||
PHY_MERGE_CODE_IGNORE_GROUP_ID,
|
||||
PHY_MERGE_CODE_INPUT_WITH_GROUP_ID,
|
||||
PHY_MERGE_CODE_TYPE,
|
||||
};
|
||||
|
||||
static int32_t physiMergeNodeToMsg(const void* pObj, STlvEncoder* pEncoder) {
|
||||
|
@ -2717,6 +2718,9 @@ static int32_t physiMergeNodeToMsg(const void* pObj, STlvEncoder* pEncoder) {
|
|||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = tlvEncodeBool(pEncoder, PHY_MERGE_CODE_INPUT_WITH_GROUP_ID, pNode->inputWithGroupId);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = tlvEncodeI32(pEncoder, PHY_MERGE_CODE_TYPE, pNode->type);
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
@ -2752,6 +2756,9 @@ static int32_t msgToPhysiMergeNode(STlvDecoder* pDecoder, void* pObj) {
|
|||
case PHY_MERGE_CODE_INPUT_WITH_GROUP_ID:
|
||||
code = tlvDecodeBool(pTlv, &pNode->inputWithGroupId);
|
||||
break;
|
||||
case PHY_MERGE_CODE_TYPE:
|
||||
code = tlvDecodeI32(pTlv, (int32_t*)&pNode->type);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1572,6 +1572,19 @@ int32_t nodesListStrictAppendList(SNodeList* pTarget, SNodeList* pSrc) {
|
|||
return code;
|
||||
}
|
||||
|
||||
|
||||
int32_t nodesListMakeStrictAppendList(SNodeList** pTarget, SNodeList* pSrc) {
|
||||
if (NULL == *pTarget) {
|
||||
*pTarget = nodesMakeList();
|
||||
if (NULL == *pTarget) {
|
||||
terrno = TSDB_CODE_OUT_OF_MEMORY;
|
||||
return TSDB_CODE_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
return nodesListStrictAppendList(*pTarget, pSrc);
|
||||
}
|
||||
|
||||
|
||||
int32_t nodesListPushFront(SNodeList* pList, SNode* pNode) {
|
||||
if (NULL == pList || NULL == pNode) {
|
||||
return TSDB_CODE_FAILED;
|
||||
|
|
|
@ -43,6 +43,7 @@ int32_t optimizeLogicPlan(SPlanContext* pCxt, SLogicSubplan* pLogicSubplan);
|
|||
int32_t splitLogicPlan(SPlanContext* pCxt, SLogicSubplan* pLogicSubplan);
|
||||
int32_t scaleOutLogicPlan(SPlanContext* pCxt, SLogicSubplan* pLogicSubplan, SQueryLogicPlan** pLogicPlan);
|
||||
int32_t createPhysiPlan(SPlanContext* pCxt, SQueryLogicPlan* pLogicPlan, SQueryPlan** pPlan, SArray* pExecNodeList);
|
||||
int32_t validateQueryPlan(SPlanContext* pCxt, SQueryPlan* pPlan);
|
||||
|
||||
bool getBatchScanOptionFromHint(SNodeList* pList);
|
||||
bool getSortForGroupOptHint(SNodeList* pList);
|
||||
|
|
|
@ -747,6 +747,7 @@ static int32_t createAggLogicNode(SLogicPlanContext* pCxt, SSelectStmt* pSelect,
|
|||
|
||||
pAgg->isGroupTb = pAgg->pGroupKeys ? keysHasTbname(pAgg->pGroupKeys) : 0;
|
||||
pAgg->isPartTb = pSelect->pPartitionByList ? keysHasTbname(pSelect->pPartitionByList) : 0;
|
||||
pAgg->hasGroup = pAgg->pGroupKeys || pSelect->pPartitionByList;
|
||||
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
*pLogicNode = (SLogicNode*)pAgg;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#define OPTIMIZE_FLAG_PUSH_DOWN_CONDE OPTIMIZE_FLAG_MASK(1)
|
||||
|
||||
#define OPTIMIZE_FLAG_SET_MASK(val, mask) (val) |= (mask)
|
||||
#define OPTIMIZE_FLAG_CLEAR_MASK(val, mask) (val) &= (~(mask))
|
||||
#define OPTIMIZE_FLAG_TEST_MASK(val, mask) (((val) & (mask)) != 0)
|
||||
|
||||
typedef struct SOptimizeContext {
|
||||
|
@ -2499,21 +2500,7 @@ static bool lastRowScanOptCheckColNum(int32_t lastColNum, col_id_t lastColId,
|
|||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static bool lastRowScanOptCheckFuncList(SLogicNode* pNode, bool* hasOtherFunc) {
|
||||
bool hasNonPKSelectFunc = false;
|
||||
SNode* pFunc = NULL;
|
||||
int32_t lastColNum = 0, selectNonPKColNum = 0;
|
||||
|
@ -2559,8 +2546,43 @@ static bool lastRowScanOptMayBeOptimized(SLogicNode* pNode) {
|
|||
return false;
|
||||
}
|
||||
} else if (FUNCTION_TYPE_LAST_ROW != pAggFunc->funcType) {
|
||||
*hasOtherFunc = true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool lastRowScanOptCheckLastCache(SAggLogicNode* pAgg, SScanLogicNode* pScan) {
|
||||
// Only one of LAST and LASTROW can appear
|
||||
if (pAgg->hasLastRow == pAgg->hasLast || (!pAgg->hasLast && !pAgg->hasLastRow) || NULL != pAgg->pGroupKeys || NULL != pScan->node.pConditions ||
|
||||
!hasSuitableCache(pScan->cacheLastMode, pAgg->hasLastRow, pAgg->hasLast) ||
|
||||
IS_TSWINDOW_SPECIFIED(pScan->scanRange)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!lastRowScanOptCheckLastCache(pAgg, pScan)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasOtherFunc = false;
|
||||
if (!lastRowScanOptCheckFuncList(pNode, &hasOtherFunc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasOtherFunc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -2569,6 +2591,7 @@ static bool lastRowScanOptMayBeOptimized(SLogicNode* pNode) {
|
|||
typedef struct SLastRowScanOptSetColDataTypeCxt {
|
||||
bool doAgg;
|
||||
SNodeList* pLastCols;
|
||||
SNodeList* pOtherCols;
|
||||
} SLastRowScanOptSetColDataTypeCxt;
|
||||
|
||||
static EDealRes lastRowScanOptSetColDataType(SNode* pNode, void* pContext) {
|
||||
|
@ -2611,6 +2634,33 @@ static void lastRowScanOptSetLastTargets(SNodeList* pTargets, SNodeList* pLastCo
|
|||
}
|
||||
}
|
||||
|
||||
static void lastRowScanOptRemoveUslessTargets(SNodeList* pTargets, SNodeList* pList1, SNodeList* pList2) {
|
||||
SNode* pTarget = NULL;
|
||||
WHERE_EACH(pTarget, pTargets) {
|
||||
bool found = false;
|
||||
SNode* pCol = NULL;
|
||||
FOREACH(pCol, pList1) {
|
||||
if (nodesEqualNode(pCol, pTarget)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
FOREACH(pCol, pList2) {
|
||||
if (nodesEqualNode(pCol, pTarget)) {
|
||||
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);
|
||||
|
||||
|
@ -2618,7 +2668,7 @@ static int32_t lastRowScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogic
|
|||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
SLastRowScanOptSetColDataTypeCxt cxt = {.doAgg = true, .pLastCols = NULL};
|
||||
SLastRowScanOptSetColDataTypeCxt cxt = {.doAgg = true, .pLastCols = NULL, .pOtherCols = NULL};
|
||||
SNode* pNode = NULL;
|
||||
SColumnNode* pPKTsCol = NULL;
|
||||
SColumnNode* pNonPKCol = NULL;
|
||||
|
@ -2639,8 +2689,11 @@ static int32_t lastRowScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogic
|
|||
nodesWalkExpr(nodesListGetNode(pFunc->pParameterList, 0), lastRowScanOptSetColDataType, &cxt);
|
||||
nodesListErase(pFunc->pParameterList, nodesListGetCell(pFunc->pParameterList, 1));
|
||||
}
|
||||
} else if (FUNCTION_TYPE_SELECT_VALUE == funcType) {
|
||||
} else {
|
||||
pNode = nodesListGetNode(pFunc->pParameterList, 0);
|
||||
nodesListMakeAppend(&cxt.pOtherCols, pNode);
|
||||
|
||||
if (FUNCTION_TYPE_SELECT_VALUE == funcType) {
|
||||
if (nodeType(pNode) == QUERY_NODE_COLUMN) {
|
||||
SColumnNode* pCol = (SColumnNode*)pNode;
|
||||
if (pCol->colId == PRIMARYKEY_TIMESTAMP_COL_ID) {
|
||||
|
@ -2651,6 +2704,7 @@ static int32_t lastRowScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogic
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(pAgg->node.pChildren, 0);
|
||||
pScan->scanType = SCAN_TYPE_LAST_ROW;
|
||||
|
@ -2660,6 +2714,7 @@ static int32_t lastRowScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogic
|
|||
lastRowScanOptSetLastTargets(pScan->pScanCols, cxt.pLastCols, true);
|
||||
nodesWalkExprs(pScan->pScanPseudoCols, lastRowScanOptSetColDataType, &cxt);
|
||||
lastRowScanOptSetLastTargets(pScan->node.pTargets, cxt.pLastCols, false);
|
||||
lastRowScanOptRemoveUslessTargets(pScan->node.pTargets, cxt.pLastCols, cxt.pOtherCols);
|
||||
if (pPKTsCol && pScan->node.pTargets->length == 1) {
|
||||
// when select last(ts),ts from ..., we add another ts to targets
|
||||
sprintf(pPKTsCol->colName, "#sel_val.%p", pPKTsCol);
|
||||
|
@ -2679,6 +2734,208 @@ static int32_t lastRowScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogic
|
|||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static bool splitCacheLastFuncOptMayBeOptimized(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);
|
||||
if (!lastRowScanOptCheckLastCache(pAgg, pScan)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasOtherFunc = false;
|
||||
if (!lastRowScanOptCheckFuncList(pNode, &hasOtherFunc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pAgg->hasGroup || !hasOtherFunc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int32_t splitCacheLastFuncOptCreateAggLogicNode(SAggLogicNode** pNewAgg, SAggLogicNode* pAgg, SNodeList* pFunc, SNodeList* pTargets) {
|
||||
SAggLogicNode* pNew = (SAggLogicNode*)nodesMakeNode(QUERY_NODE_LOGIC_PLAN_AGG);
|
||||
if (NULL == pNew) {
|
||||
nodesDestroyList(pFunc);
|
||||
nodesDestroyList(pTargets);
|
||||
return TSDB_CODE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
pNew->hasLastRow = false;
|
||||
pNew->hasLast = false;
|
||||
pNew->hasTimeLineFunc = pAgg->hasTimeLineFunc;
|
||||
pNew->hasGroupKeyOptimized = false;
|
||||
pNew->onlyHasKeepOrderFunc = pAgg->onlyHasKeepOrderFunc;
|
||||
pNew->node.groupAction = pAgg->node.groupAction;
|
||||
pNew->node.requireDataOrder = pAgg->node.requireDataOrder;
|
||||
pNew->node.resultDataOrder = pAgg->node.resultDataOrder;
|
||||
pNew->node.pTargets = pTargets;
|
||||
pNew->pAggFuncs = pFunc;
|
||||
pNew->pGroupKeys = nodesCloneList(pAgg->pGroupKeys);
|
||||
pNew->node.pConditions = nodesCloneNode(pAgg->node.pConditions);
|
||||
pNew->isGroupTb = pAgg->isGroupTb;
|
||||
pNew->isPartTb = pAgg->isPartTb;
|
||||
pNew->hasGroup = pAgg->hasGroup;
|
||||
pNew->node.pChildren = nodesCloneList(pAgg->node.pChildren);
|
||||
|
||||
SNode* pNode = NULL;
|
||||
FOREACH(pNode, pNew->node.pChildren) {
|
||||
if (QUERY_NODE_LOGIC_PLAN_SCAN == nodeType(pNode)) {
|
||||
OPTIMIZE_FLAG_CLEAR_MASK(((SScanLogicNode*)pNode)->node.optimizedFlag, OPTIMIZE_FLAG_SCAN_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
*pNewAgg = pNew;
|
||||
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
static int32_t splitCacheLastFuncOptModifyAggLogicNode(SAggLogicNode* pAgg) {
|
||||
pAgg->hasTimeLineFunc = false;
|
||||
pAgg->onlyHasKeepOrderFunc = true;
|
||||
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
static int32_t splitCacheLastFuncOptCreateMergeLogicNode(SMergeLogicNode** pNew, SAggLogicNode* pAgg1, SAggLogicNode* pAgg2) {
|
||||
SMergeLogicNode* pMerge = (SMergeLogicNode*)nodesMakeNode(QUERY_NODE_LOGIC_PLAN_MERGE);
|
||||
if (NULL == pMerge) {
|
||||
return TSDB_CODE_OUT_OF_MEMORY;
|
||||
}
|
||||
pMerge->colsMerge = true;
|
||||
pMerge->numOfChannels = 2;
|
||||
pMerge->srcGroupId = -1;
|
||||
pMerge->node.precision = pAgg1->node.precision;
|
||||
|
||||
SNode* pNewAgg1 = nodesCloneNode((SNode*)pAgg1);
|
||||
SNode* pNewAgg2 = nodesCloneNode((SNode*)pAgg2);
|
||||
if (NULL == pNewAgg1 || NULL == pNewAgg2) {
|
||||
nodesDestroyNode(pNewAgg1);
|
||||
nodesDestroyNode(pNewAgg2);
|
||||
return TSDB_CODE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
((SAggLogicNode*)pNewAgg1)->node.pParent = (SLogicNode*)pMerge;
|
||||
((SAggLogicNode*)pNewAgg2)->node.pParent = (SLogicNode*)pMerge;
|
||||
|
||||
SNode* pNode = NULL;
|
||||
FOREACH(pNode, ((SAggLogicNode*)pNewAgg1)->node.pChildren) {
|
||||
((SLogicNode*)pNode)->pParent = (SLogicNode*)pNewAgg1;
|
||||
}
|
||||
FOREACH(pNode, ((SAggLogicNode*)pNewAgg2)->node.pChildren) {
|
||||
((SLogicNode*)pNode)->pParent = (SLogicNode*)pNewAgg2;
|
||||
}
|
||||
|
||||
int32_t code = nodesListMakeStrictAppendList(&pMerge->node.pTargets, nodesCloneList(pAgg1->node.pTargets));
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = nodesListMakeStrictAppendList(&pMerge->node.pTargets, nodesCloneList(pAgg2->node.pTargets));
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = nodesListMakeStrictAppend(&pMerge->node.pChildren, pNewAgg1);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = nodesListMakeStrictAppend(&pMerge->node.pChildren, pNewAgg2);
|
||||
}
|
||||
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
nodesDestroyNode(pNewAgg1);
|
||||
nodesDestroyNode(pNewAgg2);
|
||||
nodesDestroyNode((SNode*)pMerge);
|
||||
} else {
|
||||
*pNew = pMerge;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
static int32_t splitCacheLastFuncOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
|
||||
SAggLogicNode* pAgg = (SAggLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, splitCacheLastFuncOptMayBeOptimized);
|
||||
|
||||
if (NULL == pAgg) {
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
SNode* pNode = NULL;
|
||||
SNodeList* pAggFuncList = NULL;
|
||||
{
|
||||
WHERE_EACH(pNode, pAgg->pAggFuncs) {
|
||||
SFunctionNode* pFunc = (SFunctionNode*)pNode;
|
||||
int32_t funcType = pFunc->funcType;
|
||||
if (FUNCTION_TYPE_LAST_ROW != funcType && FUNCTION_TYPE_LAST != funcType &&
|
||||
FUNCTION_TYPE_SELECT_VALUE != funcType && FUNCTION_TYPE_GROUP_KEY != funcType) {
|
||||
nodesListMakeStrictAppend(&pAggFuncList, nodesCloneNode(pNode));
|
||||
ERASE_NODE(pAgg->pAggFuncs);
|
||||
continue;
|
||||
}
|
||||
WHERE_NEXT;
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL == pAggFuncList) {
|
||||
planError("empty agg func list while splite projections, funcNum:%d", pAgg->pAggFuncs->length);
|
||||
return TSDB_CODE_PLAN_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
SNodeList* pTargets = NULL;
|
||||
{
|
||||
WHERE_EACH(pNode, pAgg->node.pTargets) {
|
||||
SColumnNode* pCol = (SColumnNode*)pNode;
|
||||
SNode* pFuncNode = NULL;
|
||||
bool found = false;
|
||||
FOREACH(pFuncNode, pAggFuncList) {
|
||||
SFunctionNode* pFunc = (SFunctionNode*)pFuncNode;
|
||||
if (0 == strcmp(pFunc->node.aliasName, pCol->colName)) {
|
||||
nodesListMakeStrictAppend(&pTargets, nodesCloneNode(pNode));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
ERASE_NODE(pAgg->node.pTargets);
|
||||
continue;
|
||||
}
|
||||
WHERE_NEXT;
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL == pTargets) {
|
||||
planError("empty target func list while splite projections, targetsNum:%d", pAgg->node.pTargets->length);
|
||||
nodesDestroyList(pAggFuncList);
|
||||
return TSDB_CODE_PLAN_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
SMergeLogicNode* pMerge = NULL;
|
||||
SAggLogicNode* pNewAgg = NULL;
|
||||
int32_t code = splitCacheLastFuncOptCreateAggLogicNode(&pNewAgg, pAgg, pAggFuncList, pTargets);
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = splitCacheLastFuncOptModifyAggLogicNode(pAgg);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = splitCacheLastFuncOptCreateMergeLogicNode(&pMerge, pNewAgg, pAgg);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = replaceLogicNode(pLogicSubplan, (SLogicNode*)pAgg, (SLogicNode*)pMerge);
|
||||
}
|
||||
|
||||
nodesDestroyNode((SNode *)pAgg);
|
||||
nodesDestroyNode((SNode *)pNewAgg);
|
||||
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
nodesDestroyNode((SNode *)pMerge);
|
||||
}
|
||||
|
||||
pCxt->optimized = true;
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// merge projects
|
||||
static bool mergeProjectsMayBeOptimized(SLogicNode* pNode) {
|
||||
if (QUERY_NODE_LOGIC_PLAN_PROJECT != nodeType(pNode) || 1 != LIST_LENGTH(pNode->pChildren)) {
|
||||
|
@ -3762,6 +4019,7 @@ static const SOptimizeRule optimizeRuleSet[] = {
|
|||
{.pName = "MergeProjects", .optimizeFunc = mergeProjectsOptimize},
|
||||
{.pName = "RewriteTail", .optimizeFunc = rewriteTailOptimize},
|
||||
{.pName = "RewriteUnique", .optimizeFunc = rewriteUniqueOptimize},
|
||||
{.pName = "splitCacheLastFunc", .optimizeFunc = splitCacheLastFuncOptimize},
|
||||
{.pName = "LastRowScan", .optimizeFunc = lastRowScanOptimize},
|
||||
{.pName = "TagScan", .optimizeFunc = tagScanOptimize},
|
||||
{.pName = "TableCountScan", .optimizeFunc = tableCountScanOptimize},
|
||||
|
|
|
@ -1951,20 +1951,30 @@ static int32_t createExchangePhysiNodeByMerge(SMergePhysiNode* pMerge) {
|
|||
return nodesListMakeStrictAppend(&pMerge->node.pChildren, (SNode*)pExchange);
|
||||
}
|
||||
|
||||
static int32_t createMergePhysiNode(SPhysiPlanContext* pCxt, SMergeLogicNode* pMergeLogicNode, SPhysiNode** pPhyNode) {
|
||||
static int32_t createMergePhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren, SMergeLogicNode* pMergeLogicNode, SPhysiNode** pPhyNode) {
|
||||
int32_t code = TSDB_CODE_SUCCESS;
|
||||
SMergePhysiNode* pMerge =
|
||||
(SMergePhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pMergeLogicNode, QUERY_NODE_PHYSICAL_PLAN_MERGE);
|
||||
if (NULL == pMerge) {
|
||||
return TSDB_CODE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (pMergeLogicNode->colsMerge) {
|
||||
pMerge->type = MERGE_TYPE_COLUMNS;
|
||||
} else if (pMergeLogicNode->needSort) {
|
||||
pMerge->type = MERGE_TYPE_SORT;
|
||||
} else {
|
||||
pMerge->type = MERGE_TYPE_NON_SORT;
|
||||
}
|
||||
|
||||
pMerge->numOfChannels = pMergeLogicNode->numOfChannels;
|
||||
pMerge->srcGroupId = pMergeLogicNode->srcGroupId;
|
||||
pMerge->groupSort = pMergeLogicNode->groupSort;
|
||||
pMerge->ignoreGroupId = pMergeLogicNode->ignoreGroupId;
|
||||
pMerge->inputWithGroupId = pMergeLogicNode->inputWithGroupId;
|
||||
|
||||
int32_t code = addDataBlockSlots(pCxt, pMergeLogicNode->pInputs, pMerge->node.pOutputDataBlockDesc);
|
||||
if (!pMergeLogicNode->colsMerge) {
|
||||
code = addDataBlockSlots(pCxt, pMergeLogicNode->pInputs, pMerge->node.pOutputDataBlockDesc);
|
||||
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
for (int32_t i = 0; i < pMerge->numOfChannels; ++i) {
|
||||
|
@ -1987,6 +1997,15 @@ static int32_t createMergePhysiNode(SPhysiPlanContext* pCxt, SMergeLogicNode* pM
|
|||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = addDataBlockSlots(pCxt, pMerge->pTargets, pMerge->node.pOutputDataBlockDesc);
|
||||
}
|
||||
} else {
|
||||
SDataBlockDescNode* pLeftDesc = ((SPhysiNode*)nodesListGetNode(pChildren, 0))->pOutputDataBlockDesc;
|
||||
SDataBlockDescNode* pRightDesc = ((SPhysiNode*)nodesListGetNode(pChildren, 1))->pOutputDataBlockDesc;
|
||||
|
||||
code = setListSlotId(pCxt, pLeftDesc->dataBlockId, pRightDesc->dataBlockId, pMergeLogicNode->node.pTargets, &pMerge->pTargets);
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = addDataBlockSlots(pCxt, pMerge->pTargets, pMerge->node.pOutputDataBlockDesc);
|
||||
}
|
||||
}
|
||||
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
*pPhyNode = (SPhysiNode*)pMerge;
|
||||
|
@ -2023,7 +2042,7 @@ static int32_t doCreatePhysiNode(SPhysiPlanContext* pCxt, SLogicNode* pLogicNode
|
|||
case QUERY_NODE_LOGIC_PLAN_INTERP_FUNC:
|
||||
return createInterpFuncPhysiNode(pCxt, pChildren, (SInterpFuncLogicNode*)pLogicNode, pPhyNode);
|
||||
case QUERY_NODE_LOGIC_PLAN_MERGE:
|
||||
return createMergePhysiNode(pCxt, (SMergeLogicNode*)pLogicNode, pPhyNode);
|
||||
return createMergePhysiNode(pCxt, pChildren, (SMergeLogicNode*)pLogicNode, pPhyNode);
|
||||
case QUERY_NODE_LOGIC_PLAN_GROUP_CACHE:
|
||||
return createGroupCachePhysiNode(pCxt, pChildren, (SGroupCacheLogicNode*)pLogicNode, pPhyNode);
|
||||
case QUERY_NODE_LOGIC_PLAN_DYN_QUERY_CTRL:
|
||||
|
|
|
@ -248,8 +248,6 @@ static bool stbSplHasMultiTbScan(bool streamQuery, SLogicNode* pNode) {
|
|||
}
|
||||
if (QUERY_NODE_LOGIC_PLAN_SCAN == nodeType(pChild) && stbSplIsMultiTbScan(streamQuery, (SScanLogicNode*)pChild)) {
|
||||
return true;
|
||||
} else if (QUERY_NODE_LOGIC_PLAN_SORT == nodeType(pChild)) {
|
||||
return stbSplHasMultiTbScan(streamQuery, (SLogicNode*)pChild);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -540,11 +538,12 @@ static int32_t stbSplRewriteFromMergeNode(SMergeLogicNode* pMerge, SLogicNode* p
|
|||
}
|
||||
|
||||
static int32_t stbSplCreateMergeNode(SSplitContext* pCxt, SLogicSubplan* pSubplan, SLogicNode* pSplitNode,
|
||||
SNodeList* pMergeKeys, SLogicNode* pPartChild, bool groupSort) {
|
||||
SNodeList* pMergeKeys, SLogicNode* pPartChild, bool groupSort, bool needSort) {
|
||||
SMergeLogicNode* pMerge = (SMergeLogicNode*)nodesMakeNode(QUERY_NODE_LOGIC_PLAN_MERGE);
|
||||
if (NULL == pMerge) {
|
||||
return TSDB_CODE_OUT_OF_MEMORY;
|
||||
}
|
||||
pMerge->needSort = needSort;
|
||||
pMerge->numOfChannels = stbSplGetNumOfVgroups(pPartChild);
|
||||
pMerge->srcGroupId = pCxt->groupId;
|
||||
pMerge->node.precision = pPartChild->precision;
|
||||
|
@ -621,7 +620,7 @@ static int32_t stbSplSplitIntervalForBatch(SSplitContext* pCxt, SStableSplitInfo
|
|||
code = stbSplCreateMergeKeysByPrimaryKey(((SWindowLogicNode*)pInfo->pSplitNode)->pTspk,
|
||||
((SWindowLogicNode*)pInfo->pSplitNode)->node.outputTsOrder, &pMergeKeys);
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = stbSplCreateMergeNode(pCxt, NULL, pInfo->pSplitNode, pMergeKeys, pPartWindow, true);
|
||||
code = stbSplCreateMergeNode(pCxt, NULL, pInfo->pSplitNode, pMergeKeys, pPartWindow, true, true);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
nodesDestroyList(pMergeKeys);
|
||||
|
@ -712,7 +711,7 @@ static int32_t stbSplSplitSessionOrStateForBatch(SSplitContext* pCxt, SStableSpl
|
|||
((SWindowLogicNode*)pWindow)->node.inputTsOrder, &pMergeKeys);
|
||||
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = stbSplCreateMergeNode(pCxt, pInfo->pSubplan, pChild, pMergeKeys, (SLogicNode*)pChild, true);
|
||||
code = stbSplCreateMergeNode(pCxt, pInfo->pSubplan, pChild, pMergeKeys, (SLogicNode*)pChild, true, true);
|
||||
}
|
||||
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
|
@ -982,7 +981,7 @@ static int32_t stbSplAggNodeCreateMerge(SSplitContext* pCtx, SStableSplitInfo* p
|
|||
}
|
||||
}
|
||||
}
|
||||
code = stbSplCreateMergeNode(pCtx, NULL, pInfo->pSplitNode, pMergeKeys, pChildAgg, groupSort);
|
||||
code = stbSplCreateMergeNode(pCtx, NULL, pInfo->pSplitNode, pMergeKeys, pChildAgg, groupSort, true);
|
||||
if (TSDB_CODE_SUCCESS == code && sortForGroup) {
|
||||
SMergeLogicNode* pMerge =
|
||||
(SMergeLogicNode*)nodesListGetNode(pInfo->pSplitNode->pChildren, LIST_LENGTH(pInfo->pSplitNode->pChildren) - 1);
|
||||
|
@ -1145,7 +1144,7 @@ static int32_t stbSplSplitSortNode(SSplitContext* pCxt, SStableSplitInfo* pInfo)
|
|||
bool groupSort = ((SSortLogicNode*)pInfo->pSplitNode)->groupSort;
|
||||
int32_t code = stbSplCreatePartSortNode((SSortLogicNode*)pInfo->pSplitNode, &pPartSort, &pMergeKeys);
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = stbSplCreateMergeNode(pCxt, pInfo->pSubplan, pInfo->pSplitNode, pMergeKeys, pPartSort, groupSort);
|
||||
code = stbSplCreateMergeNode(pCxt, pInfo->pSubplan, pInfo->pSplitNode, pMergeKeys, pPartSort, groupSort, true);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
nodesDestroyNode((SNode*)pInfo->pSplitNode);
|
||||
|
@ -1195,7 +1194,7 @@ static int32_t stbSplSplitScanNodeWithPartTags(SSplitContext* pCxt, SStableSplit
|
|||
SLogicNode* pSplitNode = NULL;
|
||||
int32_t code = stbSplGetSplitNodeForScan(pInfo, &pSplitNode);
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = stbSplCreateMergeNode(pCxt, pInfo->pSubplan, pSplitNode, NULL, pSplitNode, true);
|
||||
code = stbSplCreateMergeNode(pCxt, pInfo->pSubplan, pSplitNode, NULL, pSplitNode, true, true);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = nodesListMakeStrictAppend(&pInfo->pSubplan->pChildren,
|
||||
|
@ -1269,7 +1268,7 @@ static int32_t stbSplSplitMergeScanNode(SSplitContext* pCxt, SLogicSubplan* pSub
|
|||
((SLimitNode*)pMergeScan->pLimit)->limit += ((SLimitNode*)pMergeScan->pLimit)->offset;
|
||||
((SLimitNode*)pMergeScan->pLimit)->offset = 0;
|
||||
}
|
||||
code = stbSplCreateMergeNode(pCxt, pSubplan, (SLogicNode*)pScan, pMergeKeys, pMergeScan, groupSort);
|
||||
code = stbSplCreateMergeNode(pCxt, pSubplan, (SLogicNode*)pScan, pMergeKeys, pMergeScan, groupSort, true);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
nodesDestroyNode((SNode*)pScan);
|
||||
|
@ -1345,7 +1344,7 @@ static int32_t stbSplSplitPartitionNode(SSplitContext* pCxt, SStableSplitInfo* p
|
|||
code = stbSplCreateMergeKeysForPartitionNode(pInfo->pSplitNode, &pMergeKeys);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = stbSplCreateMergeNode(pCxt, pInfo->pSubplan, pInfo->pSplitNode, pMergeKeys, pInfo->pSplitNode, true);
|
||||
code = stbSplCreateMergeNode(pCxt, pInfo->pSubplan, pInfo->pSplitNode, pMergeKeys, pInfo->pSplitNode, true, true);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = nodesListMakeStrictAppend(&pInfo->pSubplan->pChildren,
|
||||
|
@ -1587,10 +1586,13 @@ typedef struct SSmaIndexSplitInfo {
|
|||
static bool smaIdxSplFindSplitNode(SSplitContext* pCxt, SLogicSubplan* pSubplan, SLogicNode* pNode,
|
||||
SSmaIndexSplitInfo* pInfo) {
|
||||
if (QUERY_NODE_LOGIC_PLAN_MERGE == nodeType(pNode) && LIST_LENGTH(pNode->pChildren) > 1) {
|
||||
int32_t nodeType = nodeType(nodesListGetNode(pNode->pChildren, 0));
|
||||
if (nodeType == QUERY_NODE_LOGIC_PLAN_EXCHANGE || nodeType == QUERY_NODE_LOGIC_PLAN_MERGE) {
|
||||
pInfo->pMerge = (SMergeLogicNode*)pNode;
|
||||
pInfo->pSubplan = pSubplan;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "planInt.h"
|
||||
|
||||
#include "catalog.h"
|
||||
#include "functionMgt.h"
|
||||
#include "systable.h"
|
||||
#include "tglobal.h"
|
||||
|
||||
typedef struct SValidatePlanContext {
|
||||
SPlanContext* pPlanCxt;
|
||||
int32_t errCode;
|
||||
} SValidatePlanContext;
|
||||
|
||||
int32_t doValidatePhysiNode(SValidatePlanContext* pCxt, SNode* pNode);
|
||||
|
||||
int32_t validateMergePhysiNode(SValidatePlanContext* pCxt, SMergePhysiNode* pMerge) {
|
||||
if ((NULL != pMerge->node.pLimit || NULL != pMerge->node.pSlimit) && pMerge->type == MERGE_TYPE_NON_SORT) {
|
||||
planError("no limit&slimit supported for non sort merge, pLimit:%p", pMerge->node.pLimit);
|
||||
return TSDB_CODE_PLAN_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t validateSubplanNode(SValidatePlanContext* pCxt, SSubplan* pSubPlan) {
|
||||
if (SUBPLAN_TYPE_MODIFY == pSubPlan->subplanType) {
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
return doValidatePhysiNode(pCxt, (SNode*)pSubPlan->pNode);
|
||||
}
|
||||
|
||||
int32_t validateQueryPlanNode(SValidatePlanContext* pCxt, SQueryPlan* pPlan) {
|
||||
int32_t code = TSDB_CODE_SUCCESS;
|
||||
SNode* pNode = NULL;
|
||||
FOREACH(pNode, pPlan->pSubplans) {
|
||||
if (QUERY_NODE_NODE_LIST != nodeType(pNode)) {
|
||||
code = TSDB_CODE_PLAN_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
SNode* pSubNode = NULL;
|
||||
SNodeListNode* pSubplans = (SNodeListNode*)pNode;
|
||||
FOREACH(pSubNode, pSubplans->pNodeList) {
|
||||
if (QUERY_NODE_PHYSICAL_SUBPLAN != nodeType(pNode)) {
|
||||
code = TSDB_CODE_PLAN_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
code = doValidatePhysiNode(pCxt, pSubNode);
|
||||
if (code) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
int32_t doValidatePhysiNode(SValidatePlanContext* pCxt, SNode* pNode) {
|
||||
switch (nodeType(pNode)) {
|
||||
case QUERY_NODE_PHYSICAL_PLAN_TAG_SCAN:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_TABLE_SCAN:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_TABLE_SEQ_SCAN:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_TABLE_MERGE_SCAN:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_STREAM_SCAN:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_SYSTABLE_SCAN:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_BLOCK_DIST_SCAN:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_LAST_ROW_SCAN:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_PROJECT:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_MERGE_JOIN:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_HASH_AGG:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_EXCHANGE:
|
||||
break;
|
||||
case QUERY_NODE_PHYSICAL_PLAN_MERGE:
|
||||
return validateMergePhysiNode(pCxt, (SMergePhysiNode*)pNode);
|
||||
case QUERY_NODE_PHYSICAL_PLAN_SORT:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_GROUP_SORT:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_HASH_INTERVAL:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_MERGE_INTERVAL:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_MERGE_ALIGNED_INTERVAL:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_STREAM_INTERVAL:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_STREAM_FINAL_INTERVAL:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_STREAM_SEMI_INTERVAL:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_FILL:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_STREAM_FILL:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_MERGE_SESSION:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_STREAM_SESSION:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_STREAM_SEMI_SESSION:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_STREAM_FINAL_SESSION:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_MERGE_STATE:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_STREAM_STATE:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_PARTITION:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_STREAM_PARTITION:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_INDEF_ROWS_FUNC:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_INTERP_FUNC:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_DISPATCH:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_INSERT:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_QUERY_INSERT:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_DELETE:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_TABLE_COUNT_SCAN:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_MERGE_EVENT:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_STREAM_EVENT:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_HASH_JOIN:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_GROUP_CACHE:
|
||||
case QUERY_NODE_PHYSICAL_PLAN_DYN_QUERY_CTRL:
|
||||
break;
|
||||
case QUERY_NODE_PHYSICAL_SUBPLAN:
|
||||
return validateSubplanNode(pCxt, (SSubplan*)pNode);
|
||||
case QUERY_NODE_PHYSICAL_PLAN:
|
||||
return validateQueryPlanNode(pCxt, (SQueryPlan *)pNode);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
static void destoryValidatePlanContext(SValidatePlanContext* pCxt) {
|
||||
|
||||
}
|
||||
|
||||
int32_t validateQueryPlan(SPlanContext* pCxt, SQueryPlan* pPlan) {
|
||||
SValidatePlanContext cxt = {.pPlanCxt = pCxt,
|
||||
.errCode = TSDB_CODE_SUCCESS
|
||||
};
|
||||
|
||||
int32_t code = TSDB_CODE_SUCCESS;
|
||||
SNode* pNode = NULL;
|
||||
FOREACH(pNode, pPlan->pSubplans) {
|
||||
if (QUERY_NODE_NODE_LIST != nodeType(pNode)) {
|
||||
code = TSDB_CODE_PLAN_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
SNode* pSubNode = NULL;
|
||||
SNodeListNode* pSubplans = (SNodeListNode*)pNode;
|
||||
FOREACH(pSubNode, pSubplans->pNodeList) {
|
||||
code = doValidatePhysiNode(&cxt, pSubNode);
|
||||
if (code) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (code) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
destoryValidatePlanContext(&cxt);
|
||||
return code;
|
||||
}
|
|
@ -57,6 +57,9 @@ int32_t qCreateQueryPlan(SPlanContext* pCxt, SQueryPlan** pPlan, SArray* pExecNo
|
|||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = createPhysiPlan(pCxt, pLogicPlan, pPlan, pExecNodeList);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = validateQueryPlan(pCxt, *pPlan);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
dumpQueryPlan(*pPlan);
|
||||
}
|
||||
|
|
|
@ -1297,6 +1297,7 @@ e
|
|||
,,y,script,./test.sh -f tsim/tagindex/add_index.sim
|
||||
,,n,script,./test.sh -f tsim/tagindex/sma_and_tag_index.sim
|
||||
,,y,script,./test.sh -f tsim/view/view.sim
|
||||
,,y,script,./test.sh -f tsim/query/cache_last.sim
|
||||
,,y,script,./test.sh -f tsim/query/const.sim
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
system sh/stop_dnodes.sh
|
||||
system sh/deploy.sh -n dnode1 -i 1
|
||||
system sh/exec.sh -n dnode1 -s start
|
||||
sql connect
|
||||
|
||||
sql drop database if exists db1;
|
||||
sql create database if not exists db1 cachemodel 'both' cachesize 10;
|
||||
sql use db1;
|
||||
sql create stable sta (ts timestamp, f1 double, f2 binary(200)) tags(t1 int);
|
||||
sql create table tba1 using sta tags(1);
|
||||
sql insert into tba1 values ('2022-04-26 15:15:01', 1.0, "a");
|
||||
sql insert into tba1 values ('2022-04-26 15:15:02', 2.0, "b");
|
||||
sql insert into tba1 values ('2022-04-26 15:15:04', 4.0, "b");
|
||||
sql insert into tba1 values ('2022-04-26 15:15:05', 5.0, "b");
|
||||
sql create table tba2 using sta tags(2);
|
||||
sql insert into tba2 values ('2022-04-26 15:15:01', 1.2, "a");
|
||||
sql insert into tba2 values ('2022-04-26 15:15:02', 2.2, "b");
|
||||
sql create table tba3 using sta tags(3);
|
||||
sql insert into tba3 values ('2022-04-26 15:15:10', 1.3, "a");
|
||||
sql insert into tba3 values ('2022-04-26 15:15:11', 2.3, "b");
|
||||
sql select count(*), last(*) from sta;
|
||||
if $rows != 1 then
|
||||
return -1
|
||||
endi
|
||||
if $data00 != 8 then
|
||||
return -1
|
||||
endi
|
||||
if $data01 != @22-04-26 15:15:11.000@ then
|
||||
return -1
|
||||
endi
|
||||
if $data02 != 2.300000000 then
|
||||
return -1
|
||||
endi
|
||||
if $data03 != b then
|
||||
return -1
|
||||
endi
|
||||
sql explain select count(*), last(*) from sta;
|
||||
if $data00 != @-> Merge (columns=4 width=226 input_order=unknown output_order=unknown mode=column)@ then
|
||||
return -1
|
||||
endi
|
||||
sql explain select first(f1), last(*) from sta;
|
||||
if $data00 != @-> Merge (columns=4 width=226 input_order=unknown output_order=unknown mode=column)@ then
|
||||
return -1
|
||||
endi
|
||||
sql select first(f1), last(*) from sta;
|
||||
if $rows != 1 then
|
||||
return -1
|
||||
endi
|
||||
sql select last_row(f1), last(f1) from sta;
|
||||
if $rows != 1 then
|
||||
return -1
|
||||
endi
|
||||
sql select count(*), last_row(f1), last(f1) from sta;
|
||||
if $rows != 1 then
|
||||
return -1
|
||||
endi
|
||||
sql explain select count(*), last_row(f1), last(f1) from sta;
|
||||
if $data00 != @-> Aggragate (functions=3 width=24 input_order=desc )@ then
|
||||
return -1
|
||||
endi
|
||||
sql_error select count(*), last_row(f1), min(f1), f1 from sta;
|
||||
sql select count(*), last_row(f1), min(f1),tbname from sta partition by tbname;
|
||||
if $rows != 3 then
|
||||
return -1
|
||||
endi
|
||||
sql explain select count(*), last_row(f1), min(f1),tbname from sta partition by tbname;
|
||||
if $data00 != @-> Data Exchange 2:1 (width=296)@ then
|
||||
return -1
|
||||
endi
|
||||
sql explain select count(*), last_row(f1), min(f1) from sta;
|
||||
if $data00 != @-> Merge (columns=3 width=24 input_order=unknown output_order=unknown mode=column)@ then
|
||||
return -1
|
||||
endi
|
||||
sql explain select count(*), last_row(f1), min(f1),tbname from sta group by tbname;
|
||||
if $data00 != @-> Data Exchange 2:1 (width=296)@ then
|
||||
return -1
|
||||
endi
|
||||
sql explain select count(*), last_row(f1), min(f1),t1 from sta partition by t1;
|
||||
if $data00 != @-> Aggragate (functions=4 width=28 input_order=desc )@ then
|
||||
return -1
|
||||
endi
|
||||
sql explain select count(*), last_row(f1), min(f1),t1 from sta group by t1;
|
||||
if $data00 != @-> Aggragate (functions=4 width=28 input_order=desc )@ then
|
||||
return -1
|
||||
endi
|
||||
sql explain select distinct count(*), last_row(f1), min(f1) from sta;
|
||||
if $data10 != @ -> Merge (columns=3 width=24 input_order=unknown output_order=unknown mode=column)@ then
|
||||
print $data10
|
||||
return -1
|
||||
endi
|
||||
sql explain select count(*), last_row(f1), min(f1) from sta interval(1s);
|
||||
if $data10 != @ -> Merge (columns=4 width=66 input_order=asc output_order=asc mode=sort)@ then
|
||||
return -1
|
||||
endi
|
||||
sql explain select distinct count(*), last_row(f1), min(f1) from tba1;
|
||||
if $data10 != @ -> Merge (columns=3 width=24 input_order=unknown output_order=unknown mode=column)@ then
|
||||
return -1
|
||||
endi
|
||||
sql select distinct count(*), last_row(f1), min(f1) from tba1;
|
||||
if $rows != 1 then
|
||||
return -1
|
||||
endi
|
||||
|
||||
|
||||
system sh/exec.sh -n dnode1 -s stop -x SIGINT
|
|
@ -210,7 +210,7 @@ class TDTestCase:
|
|||
#self.check_explain_res_has_row('SortMerge', explain_res)
|
||||
#self.check_explain_res_has_row("blocking=0", explain_res)
|
||||
explain_res = self.explain_sql(sql_hint)
|
||||
self.check_explain_res_has_row('SortMerge', explain_res)
|
||||
self.check_explain_res_has_row('Merge', explain_res)
|
||||
self.check_explain_res_has_row('blocking=0', explain_res)
|
||||
|
||||
def test_pipelined_agg_plan_with_slimit(self):
|
||||
|
|
Loading…
Reference in New Issue