• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

taosdata / TDengine / #5069

17 May 2026 01:15AM UTC coverage: 73.389% (+0.02%) from 73.368%
#5069

push

travis-ci

web-flow
feat (TDgpt): Dynamic Model Synchronization Enhancements (#35344)

* refactor: do some internal refactor.

* fix: fix multiprocess sync issue.

* feat: add dynamic anomaly detection and forecasting services

* fix: log error message for undeploying model in exception handling

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix: handle undeploy when model exists only on disk

Agent-Logs-Url: https://github.com/taosdata/TDengine/sessions/286aafa0-c3ce-4c27-b803-2707571e9dc1

Co-authored-by: hjxilinx <8252296+hjxilinx@users.noreply.github.com>

* fix: guard dynamic registry concurrent access

Agent-Logs-Url: https://github.com/taosdata/TDengine/sessions/5e4db858-6458-40f4-ac28-d1b1b7f97c18

Co-authored-by: hjxilinx <8252296+hjxilinx@users.noreply.github.com>

* fix: tighten service list locking scope

Agent-Logs-Url: https://github.com/taosdata/TDengine/sessions/5e4db858-6458-40f4-ac28-d1b1b7f97c18

Co-authored-by: hjxilinx <8252296+hjxilinx@users.noreply.github.com>

* fix: restore prophet support and update tests per review feedback

Agent-Logs-Url: https://github.com/taosdata/TDengine/sessions/92298ae1-7da6-4d07-b20e-101c7cd0b26b

Co-authored-by: hjxilinx <8252296+hjxilinx@users.noreply.github.com>

* fix: improve test name and move copy inside lock scope

Agent-Logs-Url: https://github.com/taosdata/TDengine/sessions/92298ae1-7da6-4d07-b20e-101c7cd0b26b

Co-authored-by: hjxilinx <8252296+hjxilinx@users.noreply.github.com>

* Potential fix for pull request finding

Co-au... (continued)

281664 of 383795 relevant lines covered (73.39%)

139049899.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.74
/source/libs/executor/src/sortoperator.c
1
/*
2
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
3
 *
4
 * This program is free software: you can use, redistribute, and/or modify
5
 * it under the terms of the GNU Affero General Public License, version 3
6
 * or later ("AGPL"), as published by the Free Software Foundation.
7
 *
8
 * This program is distributed in the hope that it will be useful, but WITHOUT
9
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
 * FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * You should have received a copy of the GNU Affero General Public License
13
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14
 */
15

16
#include "executorInt.h"
17
#include "filter.h"
18
#include "operator.h"
19
#include "querytask.h"
20
#include "tdatablock.h"
21

22
typedef struct SSortOpGroupIdCalc {
23
  STupleHandle* pSavedTuple;
24
  SArray*       pSortColsArr;
25
  char*         keyBuf;
26
  int32_t       lastKeysLen; // default to be 0
27
  uint64_t      lastGroupId;
28
  bool          excludePKCol;
29
} SSortOpGroupIdCalc;
30

31
typedef struct SSortOperatorInfo {
32
  SOptrBasicInfo      binfo;
33
  uint32_t            sortBufSize;  // max buffer size for in-memory sort
34
  SArray*             pSortInfo;
35
  SSortHandle*        pSortHandle;
36
  SColMatchInfo       matchInfo;
37
  int32_t             bufPageSize;
38
  int64_t             startTs;      // sort start time
39
  uint64_t            sortElapsed;  // sort elapsed time, time to flush to disk not included.
40
  SLimitInfo          limitInfo;
41
  uint64_t            maxTupleLength;
42
  int64_t             maxRows;
43
  SSortOpGroupIdCalc* pGroupIdCalc;
44
} SSortOperatorInfo;
45

46
static int32_t doSort(SOperatorInfo* pOperator, SSDataBlock** pResBlock);
47
static int32_t doOpenSortOperator(SOperatorInfo* pOperator);
48
static int32_t getExplainExecInfo(SOperatorInfo* pOptr, void** pOptrExplain, uint32_t* len);
49
static int32_t doGroupSort(SOperatorInfo* pOperator, SSDataBlock** pResBlock);
50

51
static void destroySortOperatorInfo(void* param);
52
static void calcSortOperMaxTupleLength(SSortOperatorInfo* pSortOperInfo, SNodeList* pSortKeys);
53

54
// Check whether the given slotId is the output of a Sort scalar pre-calculation expression.
55
// The planner's rewritePrecalcExprs pushes expression results (e.g. ts+1000) into extra slots
56
// in the child's output descriptor. This helper identifies such slots.
57
static bool sortIsExprResultSlot(const SOperatorInfo* pOperator, int32_t slotId) {
37,723,309✔
58
  if (pOperator == NULL || pOperator->exprSupp.pExprInfo == NULL || slotId < 0) {
37,723,309✔
59
    return false;
26,807,320✔
60
  }
61
  for (int32_t idx = 0; idx < pOperator->exprSupp.numOfExprs; ++idx) {
16,790,872✔
62
    if (pOperator->exprSupp.pExprInfo[idx].base.resSchema.slotId == slotId) {
10,937,343✔
63
      return true;
5,062,115✔
64
    }
65
  }
66
  return false;
5,853,529✔
67
}
68

69
// Find the slot index of the original (non-expression) primary timestamp column in the
70
// Sort's internal data block.  The internal block may contain both the original ts column
71
// and an expression-derived ts column (e.g. ts+1000).  We return the first TIMESTAMP
72
// column whose slot is NOT an expression result.
73
static int32_t sortFindOrigTsSlot(const SOperatorInfo* pOperator, const SSDataBlock* pBlock) {
5,062,115✔
74
  if (pBlock == NULL || pBlock->pDataBlock == NULL) {
5,062,115✔
75
    return -1;
×
76
  }
77
  int32_t colCount = (int32_t)taosArrayGetSize(pBlock->pDataBlock);
5,062,115✔
78
  for (int32_t idx = 0; idx < colCount; ++idx) {
5,062,115✔
79
    SColumnInfoData* pCol = taosArrayGet(pBlock->pDataBlock, idx);
5,062,115✔
80
    if (pCol != NULL && pCol->info.type == TSDB_DATA_TYPE_TIMESTAMP &&
5,062,115✔
81
        !sortIsExprResultSlot(pOperator, idx)) {
5,062,115✔
82
      return idx;
5,062,115✔
83
    }
84
  }
85
  return -1;
×
86
}
87

88
static void destroySortOpGroupIdCalc(SSortOpGroupIdCalc* pCalc);
89

90
static int32_t resetSortOperState(SOperatorInfo* pOper) {
335,984✔
91
  SSortOperatorInfo* pInfo = pOper->info;
335,984✔
92
  SExecTaskInfo*           pTaskInfo = pOper->pTaskInfo;
335,984✔
93
  pOper->status = OP_NOT_OPENED;
335,984✔
94

95
  resetBasicOperatorState(&pInfo->binfo);
335,984✔
96
  destroySqlFunctionCtx(pOper->exprSupp.pCtx, pOper->exprSupp.pExprInfo, pOper->exprSupp.numOfExprs);
335,984✔
97
  taosMemoryFreeClear(pOper->exprSupp.rowEntryInfoOffset);
335,984✔
98
  pOper->exprSupp.pCtx =
335,984✔
99
      createSqlFunctionCtx(pOper->exprSupp.pExprInfo, pOper->exprSupp.numOfExprs, &pOper->exprSupp.rowEntryInfoOffset, &pTaskInfo->storageAPI.functionStore);
335,984✔
100

101
  tsortDestroySortHandle(pInfo->pSortHandle);
335,984✔
102
  pInfo->pSortHandle = NULL;
335,984✔
103

104
  if (pInfo->pGroupIdCalc) {
335,984✔
105
    pInfo->pGroupIdCalc->lastGroupId = 0;
×
106
    pInfo->pGroupIdCalc->lastKeysLen = 0;
×
107
  }
108

109
  return 0;
335,984✔
110
}
111

112
// todo add limit/offset impl
113
int32_t createSortOperatorInfo(SOperatorInfo* downstream, SSortPhysiNode* pSortNode, SExecTaskInfo* pTaskInfo, SOperatorInfo** pOptrInfo) {
38,263,617✔
114
  QRY_PARAM_CHECK(pOptrInfo);
38,263,617✔
115

116
  int32_t code = 0;
38,281,555✔
117
  int32_t lino = 0;
38,281,555✔
118

119
  SSortOperatorInfo* pInfo = taosMemoryCalloc(1, sizeof(SSortOperatorInfo));
38,281,555✔
120
  SOperatorInfo*     pOperator = taosMemoryCalloc(1, sizeof(SOperatorInfo));
38,205,892✔
121
  if (pInfo == NULL || pOperator == NULL) {
38,213,972✔
122
    code = terrno;
×
123
    goto _error;
×
124
  }
125
  initOperatorCostInfo(pOperator);
38,217,363✔
126

127
  pOperator->pTaskInfo = pTaskInfo;
38,289,556✔
128
  SDataBlockDescNode* pDescNode = pSortNode->node.pOutputDataBlockDesc;
38,290,289✔
129

130
  int32_t numOfCols = 0;
38,277,574✔
131
  code = createExprInfo(pSortNode->pExprs, NULL, &pOperator->exprSupp.pExprInfo, &numOfCols);
38,294,092✔
132
  QUERY_CHECK_CODE(code, lino, _error);
38,239,911✔
133

134
  pOperator->exprSupp.numOfExprs = numOfCols;
38,239,911✔
135
  int32_t numOfOutputCols = 0;
38,216,901✔
136
  code =
137
      extractColMatchInfo(pSortNode->pTargets, pDescNode, &numOfOutputCols, COL_MATCH_FROM_SLOT_ID, &pInfo->matchInfo);
38,258,161✔
138
  if (code != TSDB_CODE_SUCCESS) {
38,276,313✔
139
    goto _error;
×
140
  }
141
  
142
  calcSortOperMaxTupleLength(pInfo, pSortNode->pSortKeys);
38,276,313✔
143
  pInfo->maxRows = -1;
38,274,080✔
144
  if (pSortNode->node.pLimit && ((SLimitNode*)pSortNode->node.pLimit)->limit) {
38,275,370✔
145
    SLimitNode* pLimit = (SLimitNode*)pSortNode->node.pLimit;
5,430,624✔
146
    if (pLimit->limit->datum.i > 0) {
5,424,057✔
147
      pInfo->maxRows = pLimit->limit->datum.i + (pLimit->offset ? pLimit->offset->datum.i : 0);
5,420,680✔
148
    }
149
  }
150

151
  pOperator->exprSupp.pCtx =
38,244,682✔
152
      createSqlFunctionCtx(pOperator->exprSupp.pExprInfo, numOfCols, &pOperator->exprSupp.rowEntryInfoOffset, &pTaskInfo->storageAPI.functionStore);
38,230,297✔
153
  QUERY_CHECK_NULL(pOperator->exprSupp.pCtx, code, lino, _error, terrno);
38,254,321✔
154
  initResultSizeInfo(&pOperator->resultInfo, 1024);
38,212,125✔
155
  code = filterInitFromNode((SNode*)pSortNode->node.pConditions, &pOperator->exprSupp.pFilterInfo, 0,
38,248,887✔
156
                            pTaskInfo->pStreamRuntimeInfo);
38,263,753✔
157
  if (code != TSDB_CODE_SUCCESS) {
38,238,414✔
158
    goto _error;
×
159
  }
160

161
  pInfo->binfo.pRes = createDataBlockFromDescNode(pDescNode);
38,238,414✔
162
  QUERY_CHECK_NULL(pInfo->binfo.pRes, code, lino, _error, terrno);
38,307,527✔
163

164
  pInfo->pSortInfo = createSortInfo(pSortNode->pSortKeys);
38,262,058✔
165
  TSDB_CHECK_NULL(pInfo->pSortInfo, code, lino, _error, terrno);
38,270,854✔
166

167
  if (pSortNode->calcGroupId) {
38,202,454✔
168
    int32_t keyLen;
126,286✔
169
    SSortOpGroupIdCalc* pGroupIdCalc = pInfo->pGroupIdCalc = taosMemoryCalloc(1, sizeof(SSortOpGroupIdCalc));
126,286✔
170
    if (!pGroupIdCalc) {
126,828✔
171
      code = terrno;
×
172
      goto _error;
×
173
    }
174
    SNodeList* pSortColsNodeArr = makeColsNodeArrFromSortKeys(pSortNode->pSortKeys);
126,828✔
175
    if (!pSortColsNodeArr) code = terrno;
127,370✔
176
    if (TSDB_CODE_SUCCESS == code) {
127,370✔
177
      pGroupIdCalc->pSortColsArr = makeColumnArrayFromList(pSortColsNodeArr);
126,828✔
178
      if (!pGroupIdCalc->pSortColsArr) code = terrno;
127,370✔
179
      nodesClearList(pSortColsNodeArr);
127,370✔
180
    }
181
    if (TSDB_CODE_SUCCESS == code) {
127,370✔
182
      // PK ts col should always at last, see partColOptCreateSort
183
      if (pSortNode->excludePkCol) taosArrayPop(pGroupIdCalc->pSortColsArr);
127,370✔
184
      code = extractKeysLen(pGroupIdCalc->pSortColsArr, &keyLen);
127,370✔
185
      QUERY_CHECK_CODE(code, lino, _error);
126,286✔
186
    }
187
    if (TSDB_CODE_SUCCESS == code) {
126,286✔
188
      pGroupIdCalc->lastKeysLen = 0;
126,828✔
189
      pGroupIdCalc->keyBuf = taosMemoryCalloc(1, keyLen);
126,828✔
190
      if (!pGroupIdCalc->keyBuf) {
126,286✔
191
        code = terrno;
×
192
      }
193
    }
194
  }
195
  if (code != TSDB_CODE_SUCCESS) goto _error;
38,228,772✔
196

197
  pInfo->binfo.inputTsOrder = pSortNode->node.inputTsOrder;
38,228,772✔
198
  pInfo->binfo.outputTsOrder = pSortNode->node.outputTsOrder;
38,221,561✔
199
  initLimitInfo(pSortNode->node.pLimit, pSortNode->node.pSlimit, &pInfo->limitInfo);
38,205,162✔
200

201
  setOperatorInfo(pOperator, "SortOperator", QUERY_NODE_PHYSICAL_PLAN_SORT, true, OP_NOT_OPENED, pInfo, pTaskInfo);
38,244,225✔
202

203

204
  // lazy evaluation for the following parameter since the input datablock is not known till now.
205
  //  pInfo->bufPageSize  = rowSize < 1024 ? 1024 * 2 : rowSize * 2;
206
  //  there are headers, so pageSize = rowSize + header pInfo->sortBufSize  = pInfo->bufPageSize * 16;
207
  // TODO dynamic set the available sort buffer
208

209
  pOperator->fpSet =
210
      createOperatorFpSet(doOpenSortOperator, doSort, NULL, destroySortOperatorInfo, optrDefaultBufFn, getExplainExecInfo, optrDefaultGetNextExtFn, NULL);
38,276,097✔
211

212
  setOperatorResetStateFn(pOperator, resetSortOperState);
38,238,260✔
213
  code = appendDownstream(pOperator, &downstream, 1);
38,242,663✔
214
  if (code != TSDB_CODE_SUCCESS) {
38,218,277✔
215
    goto _error;
×
216
  }
217

218
  *pOptrInfo = pOperator;
38,218,277✔
219
  return TSDB_CODE_SUCCESS;
38,192,390✔
220

221
_error:
27✔
222
  if (pInfo != NULL) {
×
223
    destroySortOperatorInfo(pInfo);
×
224
  }
225
  destroyOperatorAndDownstreams(pOperator, &downstream, 1);
×
226
  pTaskInfo->code = code;
×
227
  return code;
×
228
}
229

230
int32_t appendOneRowToDataBlock(SSDataBlock* pBlock, STupleHandle* pTupleHandle) {
2,147,483,647✔
231
  int32_t code = 0;
2,147,483,647✔
232
  for (int32_t i = 0; i < taosArrayGetSize(pBlock->pDataBlock); ++i) {
2,147,483,647✔
233
    SColumnInfoData* pColInfo = taosArrayGet(pBlock->pDataBlock, i);
2,147,483,647✔
234
    if (pColInfo == NULL) {
2,147,483,647✔
235
      return terrno;
×
236
    }
237

238
    bool isNull = tsortIsNullVal(pTupleHandle, i);
2,147,483,647✔
239
    if (isNull) {
2,147,483,647✔
240
      colDataSetNULL(pColInfo, pBlock->info.rows);
2,147,483,647✔
241
    } else {
242
      char* pData = NULL;
2,147,483,647✔
243
      tsortGetValue(pTupleHandle, i, (void**) &pData);
2,147,483,647✔
244

245
      if (pData != NULL) {
2,147,483,647✔
246
        code = colDataSetVal(pColInfo, pBlock->info.rows, pData, false);
2,147,483,647✔
247
        if (code) {
2,147,483,647✔
248
          return code;
×
249
        }
250
      }
251
    }
252
  }
253

254
  pBlock->info.dataLoad = 1;
2,147,483,647✔
255

256
  SDataBlockInfo info = {0};
2,147,483,647✔
257
  tsortGetBlockInfo(pTupleHandle, &info);
2,147,483,647✔
258

259
  pBlock->info.scanFlag = info.scanFlag;
2,147,483,647✔
260
  pBlock->info.rows += 1;
2,147,483,647✔
261
  return code;
2,147,483,647✔
262
}
263

264
/**
265
 * @brief get next tuple with group id attached, here assume that all tuples are sorted by group keys
266
 * @param [in, out] pBlock the output block, the group id will be saved in it
267
 * @retval NULL if next group tuple arrived and this new group tuple will be saved in pInfo.pSavedTuple
268
 */
269
static int32_t nextTupleWithGroupId(SSortHandle* pHandle, SSortOperatorInfo* pInfo, SSDataBlock* pBlock,
2,147,483,647✔
270
                                    STupleHandle** pTupleHandle) {
271
  QRY_PARAM_CHECK(pTupleHandle);
2,147,483,647✔
272

273
  int32_t       code = 0;
2,147,483,647✔
274
  STupleHandle* retTuple = pInfo->pGroupIdCalc->pSavedTuple;
2,147,483,647✔
275
  if (!retTuple) {
2,147,483,647✔
276
    code = tsortNextTuple(pHandle, &retTuple);
2,147,483,647✔
277
    if (code) {
2,147,483,647✔
278
      qError("failed to get next tuple, code:%s", tstrerror(code));
×
279
      return code;
×
280
    }
281
  }
282

283
  if (retTuple) {
2,147,483,647✔
284
    int32_t newGroup;
285
    if (pInfo->pGroupIdCalc->pSavedTuple) {
2,147,483,647✔
286
      newGroup = true;
1,801,608✔
287
      pInfo->pGroupIdCalc->pSavedTuple = NULL;
1,801,608✔
288
    } else {
289
      newGroup = tsortCompAndBuildKeys(pInfo->pGroupIdCalc->pSortColsArr, pInfo->pGroupIdCalc->keyBuf,
2,147,483,647✔
290
                                       &pInfo->pGroupIdCalc->lastKeysLen, retTuple);
2,147,483,647✔
291
    }
292

293
    bool emptyBlock = (pBlock->info.rows == 0);
2,147,483,647✔
294
    if (newGroup) {
2,147,483,647✔
295
      if (!emptyBlock) {
3,732,212✔
296
        // new group arrived, and we have already copied some tuples for cur group, save the new group tuple, return
297
        // NULL. Note that the keyBuf and lastKeysLen has been updated to new value
298
        pInfo->pGroupIdCalc->pSavedTuple = retTuple;
1,802,692✔
299
        retTuple = NULL;
1,802,150✔
300
      } else {
301
        // new group with empty block
302
        pInfo->pGroupIdCalc->lastGroupId = pBlock->info.id.groupId =
1,928,978✔
303
            calcGroupId(pInfo->pGroupIdCalc->keyBuf, pInfo->pGroupIdCalc->lastKeysLen);
1,929,520✔
304
      }
305
    } else {
306
      if (emptyBlock) {
2,147,483,647✔
307
        // new block but not new group, assign last group id to it
308
        pBlock->info.id.groupId = pInfo->pGroupIdCalc->lastGroupId;
1,098,092✔
309
      } else {
310
        // not new group and not empty block and ret NOT NULL, just return the tuple
311
      }
312
    }
313
  }
314

315
  *pTupleHandle = retTuple;
2,147,483,647✔
316
  return code;
2,147,483,647✔
317
}
318

319
static int32_t getSortedBlockData(SSortHandle* pHandle, SSDataBlock* pDataBlock, int32_t capacity, SArray* pColMatchInfo,
94,392,122✔
320
                                  const SOperatorInfo* pOperator, SSortOperatorInfo* pInfo, SSDataBlock** pResBlock) {
321
  QRY_PARAM_CHECK(pResBlock);
94,392,122✔
322
  blockDataCleanup(pDataBlock);
94,394,140✔
323

324
  int32_t       lino = 0;
94,397,344✔
325
  int32_t       code = 0;
94,397,344✔
326
  STupleHandle* pTupleHandle = NULL;
94,397,344✔
327
  SSDataBlock*  p = NULL;
94,395,852✔
328

329
  code = tsortGetSortedDataBlock(pHandle, &p);
94,396,898✔
330
  if (p == NULL || (code != 0)) {
94,396,142✔
331
    return code;
7,140,472✔
332
  }
333

334
  code = blockDataEnsureCapacity(p, capacity);
87,255,670✔
335
  QUERY_CHECK_CODE(code, lino, _error);
87,255,696✔
336

337
  while (1) {
338
    if (pInfo->pGroupIdCalc) {
2,147,483,647✔
339
      code = nextTupleWithGroupId(pHandle, pInfo, p, &pTupleHandle);
2,147,483,647✔
340
    } else {
341
      code = tsortNextTuple(pHandle, &pTupleHandle);
2,147,483,647✔
342
    }
343

344
    TSDB_CHECK_CODE(code, lino, _error);
2,147,483,647✔
345
    if (pTupleHandle == NULL) {
2,147,483,647✔
346
      break;
62,407,642✔
347
    }
348

349
    code = appendOneRowToDataBlock(p, pTupleHandle);
2,147,483,647✔
350
    QUERY_CHECK_CODE(code, lino, _error);
2,147,483,647✔
351

352
    if (p->info.rows >= capacity) {
2,147,483,647✔
353
      break;
24,849,238✔
354
    }
355
  }
356

357
  QUERY_CHECK_CODE(code, lino, _error);
87,256,880✔
358

359
  if (p->info.rows > 0) {
87,256,880✔
360
    code = blockDataEnsureCapacity(pDataBlock, capacity);
57,715,585✔
361
    QUERY_CHECK_CODE(code, lino, _error);
57,717,815✔
362

363
    // todo extract function to handle this
364
    int32_t numOfCols = taosArrayGetSize(pColMatchInfo);
57,717,815✔
365
    for (int32_t i = 0; i < numOfCols; ++i) {
332,592,107✔
366
      SColMatchItem* pmInfo = taosArrayGet(pColMatchInfo, i);
274,875,798✔
367
      QUERY_CHECK_NULL(pmInfo, code, lino, _error, terrno);
274,878,003✔
368

369
      int32_t srcSlotId = pmInfo->srcSlotId;
274,878,003✔
370

371
      // Fix: when the planner's setListSlotId resolves Sort pTargets by name, it may
372
      // mistakenly bind the primary timestamp column to the scalar expression result slot
373
      // (e.g. slot for ts+1000) instead of the original ts slot, because pushdownDataBlockSlots
374
      // added the expression slot with the same column name.  This causes the downstream
375
      // Project operator to apply the expression again (ts+1000 becomes ts+2000).
376
      // Detect this case and fall back to the original timestamp slot.
377
      if (pmInfo->colId == PRIMARYKEY_TIMESTAMP_COL_ID &&
274,877,403✔
378
          pmInfo->dataType.type == TSDB_DATA_TYPE_TIMESTAMP &&
65,455,737✔
379
          sortIsExprResultSlot(pOperator, srcSlotId)) {
32,662,182✔
380
        int32_t origSlot = sortFindOrigTsSlot(pOperator, p);
5,062,115✔
381
        if (origSlot >= 0 && origSlot != srcSlotId) {
5,062,115✔
382
          srcSlotId = origSlot;
5,062,115✔
383
        }
384
      }
385

386
      SColumnInfoData* pSrc = taosArrayGet(p->pDataBlock, srcSlotId);
274,876,725✔
387
      QUERY_CHECK_NULL(pSrc, code, lino, _error, terrno);
274,874,526✔
388

389
      SColumnInfoData* pDst = taosArrayGet(pDataBlock->pDataBlock, pmInfo->dstSlotId);
274,874,526✔
390
      QUERY_CHECK_NULL(pDst, code, lino, _error, terrno);
274,875,104✔
391

392
      code = colDataAssign(pDst, pSrc, p->info.rows, &pDataBlock->info);
274,875,104✔
393
      QUERY_CHECK_CODE(code, lino, _error);
274,874,292✔
394
    }
395

396
    pDataBlock->info.dataLoad = 1;
57,716,309✔
397
    pDataBlock->info.rows = p->info.rows;
57,714,565✔
398
    pDataBlock->info.scanFlag = p->info.scanFlag;
57,713,782✔
399
    // propagate both C-group id and baseGId from upstream
400
    pDataBlock->info.id.groupId = p->info.id.groupId;
57,708,225✔
401
    pDataBlock->info.id.baseGId = p->info.id.baseGId;
57,710,652✔
402
  }
403

404
  blockDataDestroy(p);
87,248,986✔
405
  *pResBlock = (pDataBlock->info.rows > 0) ? pDataBlock : NULL;
87,249,001✔
406
  return code;
87,244,304✔
407

408
  _error:
×
409
  qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
410

411
  blockDataDestroy(p);
×
412
  return code;
×
413
}
414

415
int32_t loadNextDataBlock(void* param, SSDataBlock** ppBlock) {
2,147,483,647✔
416
  SOperatorInfo* pOperator = (SOperatorInfo*)param;
2,147,483,647✔
417
  int32_t        code = pOperator->fpSet.getNextFn(pOperator, ppBlock);
2,147,483,647✔
418
  if (code) {
2,147,483,647✔
419
    qError("failed to get next data block from upstream, %s code:%s", __func__, tstrerror(code));
×
420
  } else {
421
    code = blockDataCheck(*ppBlock);
2,147,483,647✔
422
    if (code) {
2,147,483,647✔
423
      qError("failed to check block data, %s code:%s", __func__, tstrerror(code));
×
424
    }
425
  }
426
  return code;
2,147,483,647✔
427
}
428

429
// todo refactor: merged with fetch fp
430
void applyScalarFunction(SSDataBlock* pBlock, void* param) {
2,147,483,647✔
431
  SOperatorInfo*     pOperator = param;
2,147,483,647✔
432
  SSortOperatorInfo* pSort = pOperator->info;
2,147,483,647✔
433
  if (pOperator->exprSupp.pExprInfo != NULL && pOperator->exprSupp.numOfExprs > 0) {
2,147,483,647✔
434
    int32_t code = projectApplyFunctions(pOperator->exprSupp.pExprInfo, pBlock, pBlock, pOperator->exprSupp.pCtx,
13,916,622✔
435
                                         pOperator->exprSupp.numOfExprs, NULL,
436
                                         GET_STM_RTINFO(pOperator->pTaskInfo), pOperator->pTaskInfo);
6,959,509✔
437
    if (code != TSDB_CODE_SUCCESS) {
6,960,707✔
438
      T_LONG_JMP(pOperator->pTaskInfo->env, code);
×
439
    }
440
  }
441
}
2,147,483,647✔
442

443
int32_t doOpenSortOperator(SOperatorInfo* pOperator) {
94,458,807✔
444
  SSortOperatorInfo* pInfo = pOperator->info;
94,458,807✔
445
  SExecTaskInfo*     pTaskInfo = pOperator->pTaskInfo;
94,466,895✔
446
  int32_t            code = TSDB_CODE_SUCCESS;
94,455,717✔
447
  int32_t            lino = 0;
94,455,717✔
448
  SSortSource* pSource =NULL;
94,455,717✔
449

450
  if (pOperator->exprSupp.pFilterInfo != NULL) {
94,455,717✔
451
    filterSetExecContext(pOperator->exprSupp.pFilterInfo, pTaskInfo, isTaskKilled);
1,262✔
452
  }
453

454
  if (OPTR_IS_OPENED(pOperator)) {
94,457,904✔
455
    return code;
56,100,670✔
456
  }
457

458
  //  pInfo->binfo.pRes is not equalled to the input datablock.
459
  pInfo->pSortHandle = NULL;
38,348,419✔
460
  code =
461
      tsortCreateSortHandle(pInfo->pSortInfo, SORT_SINGLESOURCE_SORT, -1, -1, NULL, pTaskInfo->id.str, pInfo->maxRows,
76,707,847✔
462
                            pInfo->maxTupleLength, tsPQSortMemThreshold * 1024 * 1024, &pInfo->pSortHandle);
38,358,137✔
463
  QUERY_CHECK_CODE(code, lino, _end);
38,294,301✔
464

465
  tsortSetFetchRawDataFp(pInfo->pSortHandle, loadNextDataBlock, applyScalarFunction, pOperator);
38,294,301✔
466

467
  pSource = taosMemoryCalloc(1, sizeof(SSortSource));
38,265,109✔
468
  QUERY_CHECK_NULL(pSource, code, lino, _end, terrno);
38,287,760✔
469

470
  pSource->param = pOperator->pDownstream[0];
38,287,760✔
471
  pSource->onlyRef = true;
38,325,806✔
472

473
  code = tsortAddSource(pInfo->pSortHandle, pSource);
38,326,252✔
474
  QUERY_CHECK_CODE(code, lino, _end);
38,285,179✔
475
  pSource = NULL;
38,285,179✔
476

477
  code = tsortOpen(pInfo->pSortHandle);
38,285,179✔
478
  QUERY_CHECK_CODE(code, lino, _end);
38,244,189✔
479
  pOperator->status = OP_RES_TO_RETURN;
38,243,649✔
480
  OPTR_SET_OPENED(pOperator);
38,244,146✔
481

482
_end:
38,243,589✔
483
  if (pSource) {
38,243,589✔
484
    taosMemoryFree(pSource);
×
485
  }
486
  if (code != TSDB_CODE_SUCCESS) {
38,244,189✔
487
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
540✔
488
    pTaskInfo->code = code;
540✔
489
    T_LONG_JMP(pTaskInfo->env, code);
540✔
490
  }
491
  return code;
38,243,649✔
492
}
493

494
int32_t doSort(SOperatorInfo* pOperator, SSDataBlock** pResBlock) {
94,494,666✔
495
  QRY_PARAM_CHECK(pResBlock);
94,494,666✔
496
  int32_t code = TSDB_CODE_SUCCESS;
94,497,959✔
497
  int32_t lino = 0;
94,497,959✔
498
  if (pOperator->status == OP_EXEC_DONE) {
94,497,959✔
499
    return code;
38,019✔
500
  }
501

502
  SExecTaskInfo*     pTaskInfo = pOperator->pTaskInfo;
94,448,345✔
503
  SSortOperatorInfo* pInfo = pOperator->info;
94,452,428✔
504

505
  code = pOperator->fpSet._openFn(pOperator);
94,451,267✔
506
  QUERY_CHECK_CODE(code, lino, _end);
94,340,525✔
507

508
  // multi-group case not handle here
509
  SSDataBlock* pBlock = NULL;
94,340,525✔
510
  while (1) {
45,476✔
511
    if (tsortIsClosed(pInfo->pSortHandle)) {
94,389,043✔
512
      code = TSDB_CODE_TSC_QUERY_CANCELLED;
×
513
      QUERY_CHECK_CODE(code, lino, _end);
×
514
    }
515

516
    recordOpExecBeforeDownstream(pOperator);
94,394,783✔
517
    code = getSortedBlockData(pInfo->pSortHandle, pInfo->binfo.pRes, pOperator->resultInfo.capacity,
94,393,718✔
518
                                pInfo->matchInfo.pList, pOperator, pInfo, &pBlock);
519
    recordOpExecAfterDownstream(pOperator, pBlock ? pBlock->info.rows : 0);
94,384,339✔
520
    QUERY_CHECK_CODE(code, lino, _end);
94,387,654✔
521
    if (pBlock == NULL) {
94,387,654✔
522
      setOperatorCompleted(pOperator);
36,677,245✔
523
      return code;
36,680,144✔
524
    }
525

526
    code = doFilter(pBlock, pOperator->exprSupp.pFilterInfo, &pInfo->matchInfo, NULL);
57,710,409✔
527
    QUERY_CHECK_CODE(code, lino, _end);
57,709,135✔
528

529
    if (blockDataGetNumOfRows(pBlock) == 0) {
57,709,135✔
530
      continue;
×
531
    }
532

533
    // there are bugs?
534
    bool limitReached = applyLimitOffset(&pInfo->limitInfo, pBlock, pTaskInfo);
57,710,674✔
535
    if (limitReached) {
57,711,632✔
536
      resetLimitInfoForNextGroup(&pInfo->limitInfo);
3,352,865✔
537
    }
538

539
    if (pBlock->info.rows > 0) {
57,712,804✔
540
      break;
57,667,335✔
541
    }
542
  }
543

544
  *pResBlock = blockDataGetNumOfRows(pBlock) > 0 ? pBlock : NULL;
57,667,335✔
545
_end:
57,664,413✔
546
  if (code != TSDB_CODE_SUCCESS) {
57,663,041✔
547
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
1,046✔
548
    pTaskInfo->code = code;
1,046✔
549
    T_LONG_JMP(pTaskInfo->env, code);
×
550
  }
551
  return code;
57,662,121✔
552
}
553

554
void destroySortOperatorInfo(void* param) {
38,295,356✔
555
  SSortOperatorInfo* pInfo = (SSortOperatorInfo*)param;
38,295,356✔
556
  blockDataDestroy(pInfo->binfo.pRes);
38,295,356✔
557
  pInfo->binfo.pRes = NULL;
38,296,001✔
558

559
  tsortDestroySortHandle(pInfo->pSortHandle);
38,295,875✔
560
  taosArrayDestroy(pInfo->pSortInfo);
38,312,858✔
561
  taosArrayDestroy(pInfo->matchInfo.pList);
38,312,825✔
562
  destroySortOpGroupIdCalc(pInfo->pGroupIdCalc);
38,314,670✔
563
  taosMemoryFreeClear(param);
38,311,002✔
564
}
38,314,628✔
565

566
int32_t getExplainExecInfo(SOperatorInfo* pOptr, void** pOptrExplain, uint32_t* len) {
109,490✔
567
  SSortExecInfo* pInfo = taosMemoryCalloc(1, sizeof(SSortExecInfo));
109,490✔
568
  if (pInfo == NULL) {
109,490✔
569
    return terrno;
×
570
  }
571

572
  SSortOperatorInfo* pOperatorInfo = (SSortOperatorInfo*)pOptr->info;
109,490✔
573

574
  *pInfo = tsortGetSortExecInfo(pOperatorInfo->pSortHandle);
109,490✔
575
  *pOptrExplain = pInfo;
109,490✔
576
  *len = sizeof(SSortExecInfo);
109,490✔
577
  return TSDB_CODE_SUCCESS;
109,490✔
578
}
579

580
static void calcSortOperMaxTupleLength(SSortOperatorInfo* pSortOperInfo, SNodeList* pSortKeys) {
38,270,989✔
581
  SColMatchInfo* pColItem = &pSortOperInfo->matchInfo;
38,270,989✔
582
  size_t         size = taosArrayGetSize(pColItem->pList);
38,303,312✔
583
  for (size_t i = 0; i < size; ++i) {
161,307,380✔
584
    SColMatchItem* pInfo = taosArrayGet(pColItem->pList, i);
123,053,907✔
585
    if (pInfo == NULL) {
123,055,459✔
586
      continue;
×
587
    }
588

589
    pSortOperInfo->maxTupleLength += pInfo->dataType.bytes;
123,055,459✔
590
  }
591

592
  size = LIST_LENGTH(pSortKeys);
38,253,473✔
593
  for (size_t i = 0; i < size; ++i) {
93,827,889✔
594
    SOrderByExprNode* pOrderExprNode = (SOrderByExprNode*)nodesListGetNode(pSortKeys, i);
55,545,726✔
595
    pSortOperInfo->maxTupleLength += ((SColumnNode*)pOrderExprNode->pExpr)->node.resType.bytes;
55,559,713✔
596
  }
597
}
38,282,163✔
598

599
static void destroySortOpGroupIdCalc(SSortOpGroupIdCalc* pCalc) {
38,313,151✔
600
  if (pCalc) {
38,313,151✔
601
    taosArrayDestroy(pCalc->pSortColsArr);
127,912✔
602
    taosMemoryFree(pCalc->keyBuf);
127,912✔
603
    taosMemoryFree(pCalc);
127,912✔
604
  }
605
}
38,313,151✔
606

607
//=====================================================================================
608
// Group Sort Operator
609
typedef enum EChildOperatorStatus { CHILD_OP_NEW_GROUP, CHILD_OP_SAME_GROUP, CHILD_OP_FINISHED } EChildOperatorStatus;
610

611
typedef struct SGroupSortOperatorInfo {
612
  SOptrBasicInfo       binfo;
613
  SArray*              pSortInfo;
614
  SColMatchInfo        matchInfo;
615
  int64_t              startTs;
616
  uint64_t             sortElapsed;
617
  bool                 hasGroupId;
618
  uint64_t             currGroupId;
619
  uint64_t             currBaseGId;
620
  SSDataBlock*         prefetchedSortInput;
621
  SSortHandle*         pCurrSortHandle;
622
  EChildOperatorStatus childOpStatus;
623
  SSortExecInfo        sortExecInfo;
624
} SGroupSortOperatorInfo;
625

626
int32_t getGroupSortedBlockData(SSortHandle* pHandle, SSDataBlock* pDataBlock, int32_t capacity, SArray* pColMatchInfo,
2,449,873✔
627
                                SGroupSortOperatorInfo* pInfo, SSDataBlock** pResBlock) {
628
  QRY_PARAM_CHECK(pResBlock);
2,449,873✔
629

630
  blockDataCleanup(pDataBlock);
2,449,873✔
631
  int32_t code = blockDataEnsureCapacity(pDataBlock, capacity);
2,449,873✔
632
  if (code) {
2,449,873✔
633
    return code;
×
634
  }
635

636
  SSDataBlock* p = NULL;
2,449,873✔
637
  code = tsortGetSortedDataBlock(pHandle, &p);
2,449,873✔
638
  if (p == NULL || (code != 0)) {
2,449,873✔
639
    return code;
×
640
  }
641

642
  code = blockDataEnsureCapacity(p, capacity);
2,449,873✔
643
  if (code) {
2,449,873✔
644
    return code;
×
645
  }
646

647
  while (1) {
2,147,483,647✔
648
    STupleHandle* pTupleHandle = NULL;
2,147,483,647✔
649
    code = tsortNextTuple(pHandle, &pTupleHandle);
2,147,483,647✔
650
    if (pTupleHandle == NULL || code != 0) {
2,147,483,647✔
651
      break;
652
    }
653

654
    code = appendOneRowToDataBlock(p, pTupleHandle);
2,147,483,647✔
655
    if (code) {
2,147,483,647✔
656
      break;
×
657
    }
658

659
    if (p->info.rows >= capacity) {
2,147,483,647✔
660
      break;
2,144,221✔
661
    }
662
  }
663

664
  if (p->info.rows > 0) {
2,449,873✔
665
    int32_t numOfCols = taosArrayGetSize(pColMatchInfo);
2,297,047✔
666
    for (int32_t i = 0; i < numOfCols; ++i) {
6,891,141✔
667
      SColMatchItem* pmInfo = taosArrayGet(pColMatchInfo, i);
4,594,094✔
668
      if (pmInfo == NULL) {
4,594,094✔
669
        return terrno;
×
670
      }
671

672
      SColumnInfoData* pSrc = taosArrayGet(p->pDataBlock, pmInfo->srcSlotId);
4,594,094✔
673
      if (pSrc == NULL) {
4,594,094✔
674
        return terrno;
×
675
      }
676

677
      SColumnInfoData* pDst = taosArrayGet(pDataBlock->pDataBlock, pmInfo->dstSlotId);
4,594,094✔
678
      if (pDst == NULL) {
4,593,604✔
679
        return terrno;
×
680
      }
681

682
      code = colDataAssign(pDst, pSrc, p->info.rows, &pDataBlock->info);
4,593,604✔
683
      if (code) {
4,594,094✔
684
        return code;
×
685
      }
686
    }
687

688
    pDataBlock->info.rows = p->info.rows;
2,297,047✔
689
    pDataBlock->info.capacity = p->info.rows;
2,297,047✔
690
    pDataBlock->info.scanFlag = p->info.scanFlag;
2,297,047✔
691
    // propagate ids for the current group
692
    pDataBlock->info.id.groupId = pInfo->currGroupId;
2,297,047✔
693
    pDataBlock->info.id.baseGId = pInfo->currBaseGId;
2,296,557✔
694
  }
695

696
  blockDataDestroy(p);
2,449,873✔
697
  *pResBlock = (pDataBlock->info.rows > 0) ? pDataBlock : NULL;
2,449,383✔
698
  return code;
2,449,383✔
699
}
700

701
typedef struct SGroupSortSourceParam {
702
  SOperatorInfo*          childOpInfo;
703
  SGroupSortOperatorInfo* grpSortOpInfo;
704
} SGroupSortSourceParam;
705

706
int32_t fetchNextGroupSortDataBlock(void* param, SSDataBlock** ppBlock) {
123,378,079✔
707
  int32_t                 code = 0;
123,378,079✔
708
  int32_t                 lino = 0;
123,378,079✔
709
  SGroupSortSourceParam*  source = param;
123,378,079✔
710
  SGroupSortOperatorInfo* grpSortOpInfo = source->grpSortOpInfo;
123,378,079✔
711
  SSDataBlock*            block = NULL;
123,378,079✔
712

713
  QRY_PARAM_CHECK(ppBlock);
123,378,079✔
714

715
  if (grpSortOpInfo->prefetchedSortInput) {
123,378,569✔
716
    block = grpSortOpInfo->prefetchedSortInput;
151,356✔
717
    grpSortOpInfo->prefetchedSortInput = NULL;
151,846✔
718
    *ppBlock = block;
150,866✔
719
  } else {
720
    SOperatorInfo* childOp = source->childOpInfo;
123,226,723✔
721
    code = childOp->fpSet.getNextFn(childOp, &block);
123,226,723✔
722
    QUERY_CHECK_CODE(code, lino, _end);
123,226,723✔
723

724
    if (block != NULL) {
123,226,723✔
725
      code = blockDataCheck(block);
123,132,863✔
726
      QUERY_CHECK_CODE(code, lino, _end);
123,132,863✔
727
      if (block->info.id.groupId == grpSortOpInfo->currGroupId) {
123,132,863✔
728
        grpSortOpInfo->childOpStatus = CHILD_OP_SAME_GROUP;
123,073,897✔
729
        *ppBlock = block;
123,073,897✔
730
      } else {
731
        grpSortOpInfo->childOpStatus = CHILD_OP_NEW_GROUP;
58,966✔
732
        grpSortOpInfo->prefetchedSortInput = block;
58,966✔
733
      }
734
    } else {
735
      grpSortOpInfo->childOpStatus = CHILD_OP_FINISHED;
93,860✔
736
    }
737
  }
738

739
  return code;
123,379,059✔
740
_end:
×
741
  if (code != 0) {
×
742
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
743
  }
744
  return code;
×
745
}
746

747
int32_t beginSortGroup(SOperatorInfo* pOperator) {
152,826✔
748
  SGroupSortOperatorInfo* pInfo = pOperator->info;
152,826✔
749
  SExecTaskInfo*          pTaskInfo = pOperator->pTaskInfo;
152,336✔
750

751
  //  pInfo->binfo.pRes is not equalled to the input datablock.
752
  pInfo->pCurrSortHandle = NULL;
152,336✔
753

754
  int32_t code = tsortCreateSortHandle(pInfo->pSortInfo, SORT_SINGLESOURCE_SORT, -1, -1, NULL, pTaskInfo->id.str, 0, 0,
152,336✔
755
                                       0, &pInfo->pCurrSortHandle);
756
  if (code) {
151,846✔
757
    return code;
×
758
  }
759

760
  tsortSetFetchRawDataFp(pInfo->pCurrSortHandle, fetchNextGroupSortDataBlock, applyScalarFunction, pOperator);
151,846✔
761

762
  SSortSource*           ps = taosMemoryCalloc(1, sizeof(SSortSource));
150,866✔
763
  SGroupSortSourceParam* param = taosMemoryCalloc(1, sizeof(SGroupSortSourceParam));
152,336✔
764
  if (ps == NULL || param == NULL) {
152,826✔
765
    taosMemoryFree(ps);
×
766
    taosMemoryFree(param);
×
767
    return terrno;
×
768
  }
769

770
  param->childOpInfo = pOperator->pDownstream[0];
152,826✔
771
  param->grpSortOpInfo = pInfo;
151,846✔
772

773
  ps->param = param;
152,336✔
774
  ps->onlyRef = false;
151,356✔
775
  code = tsortAddSource(pInfo->pCurrSortHandle, ps);
151,846✔
776
  if (code != 0) {
152,826✔
777
    return code;
×
778
  }
779

780
  code = tsortOpen(pInfo->pCurrSortHandle);
152,826✔
781
  return code;
152,826✔
782
}
783

784
int32_t finishSortGroup(SOperatorInfo* pOperator) {
152,826✔
785
  SGroupSortOperatorInfo* pInfo = pOperator->info;
152,826✔
786

787
  SSortExecInfo sortExecInfo = tsortGetSortExecInfo(pInfo->pCurrSortHandle);
152,826✔
788

789
  pInfo->sortExecInfo.sortMethod = sortExecInfo.sortMethod;
152,826✔
790
  pInfo->sortExecInfo.sortBuffer = sortExecInfo.sortBuffer;
152,826✔
791
  pInfo->sortExecInfo.loops += sortExecInfo.loops;
152,826✔
792
  pInfo->sortExecInfo.readBytes += sortExecInfo.readBytes;
152,826✔
793
  pInfo->sortExecInfo.writeBytes += sortExecInfo.writeBytes;
152,826✔
794

795
  tsortDestroySortHandle(pInfo->pCurrSortHandle);
152,826✔
796
  pInfo->pCurrSortHandle = NULL;
152,826✔
797

798
  return TSDB_CODE_SUCCESS;
152,826✔
799
}
800

801
int32_t doGroupSort(SOperatorInfo* pOperator, SSDataBlock** pResBlock) {
2,413,407✔
802
  QRY_PARAM_CHECK(pResBlock);
2,413,407✔
803
  SExecTaskInfo*          pTaskInfo = pOperator->pTaskInfo;
2,413,407✔
804
  SGroupSortOperatorInfo* pInfo = pOperator->info;
2,413,407✔
805
  int32_t                 code = TSDB_CODE_SUCCESS;
2,413,407✔
806
  int32_t                 lino = 0;
2,413,407✔
807

808
  if (pOperator->status == OP_EXEC_DONE) {
2,413,407✔
809
    return code;
×
810
  }
811

812
  code = pOperator->fpSet._openFn(pOperator);
2,413,407✔
813
  QUERY_CHECK_CODE(code, lino, _end);
2,413,407✔
814

815
  if (!pInfo->hasGroupId) {
2,413,407✔
816
    pInfo->hasGroupId = true;
116,360✔
817

818
    pInfo->prefetchedSortInput = getNextBlockFromDownstream(pOperator, 0);
116,360✔
819
    if (pInfo->prefetchedSortInput == NULL) {
116,360✔
820
      setOperatorCompleted(pOperator);
22,500✔
821
      return code;
22,500✔
822
    }
823

824
    pInfo->currGroupId = pInfo->prefetchedSortInput->info.id.groupId;
92,880✔
825
    pInfo->currBaseGId = pInfo->prefetchedSortInput->info.id.baseGId;
92,880✔
826
    pInfo->childOpStatus = CHILD_OP_NEW_GROUP;
93,860✔
827
    code = beginSortGroup(pOperator);
92,880✔
828
    QUERY_CHECK_CODE(code, lino, _end);
93,860✔
829
  }
830

831
  SSDataBlock* pBlock = NULL;
2,390,907✔
832
  while (pInfo->pCurrSortHandle != NULL) {
2,449,873✔
833
    if (tsortIsClosed(pInfo->pCurrSortHandle)) {
2,449,873✔
834
      code = TSDB_CODE_TSC_QUERY_CANCELLED;
×
835
      QUERY_CHECK_CODE(code, lino, _end);
×
836
    }
837

838
    // beginSortGroup would fetch all child blocks of pInfo->currGroupId;
839
    if (pInfo->childOpStatus == CHILD_OP_SAME_GROUP) {
2,449,873✔
840
      code = TSDB_CODE_QRY_EXECUTOR_INTERNAL_ERROR;
×
841
      QUERY_CHECK_CODE(code, lino, _end);
×
842
    }
843

844
    recordOpExecBeforeDownstream(pOperator);
2,449,873✔
845
    code = getGroupSortedBlockData(pInfo->pCurrSortHandle, pInfo->binfo.pRes, pOperator->resultInfo.capacity,
2,449,873✔
846
                                     pInfo->matchInfo.pList, pInfo, &pBlock);
847
    recordOpExecAfterDownstream(pOperator, pBlock ? pBlock->info.rows : 0);
2,449,383✔
848
    QUERY_CHECK_CODE(code, lino, _end);
2,448,403✔
849
    if (pBlock != NULL) {
2,448,403✔
850
      // keep both ids aligned with current group
851
      pBlock->info.id.groupId = pInfo->currGroupId;
2,296,557✔
852
      pBlock->info.id.baseGId = pInfo->currBaseGId;
2,296,557✔
853
      // baseGId follows upstream; if upstream is empty here, preserve current
854
      // (no-op if not set). We cannot reconstruct baseGId here; rely on upstream propagation.
855
      pOperator->resultInfo.totalRows += pBlock->info.rows;
2,296,557✔
856
      *pResBlock = pBlock;
2,296,557✔
857
      return code;
2,296,557✔
858
    } else {
859
      if (pInfo->childOpStatus == CHILD_OP_NEW_GROUP) {
151,846✔
860
        (void) finishSortGroup(pOperator);
58,966✔
861
        pInfo->currGroupId = pInfo->prefetchedSortInput->info.id.groupId;
58,966✔
862
        pInfo->currBaseGId = pInfo->prefetchedSortInput->info.id.baseGId;
58,966✔
863
        code = beginSortGroup(pOperator);
58,966✔
864
        QUERY_CHECK_CODE(code, lino, _end);
58,966✔
865
      } else if (pInfo->childOpStatus == CHILD_OP_FINISHED) {
93,860✔
866
        (void) finishSortGroup(pOperator);
93,860✔
867
        setOperatorCompleted(pOperator);
93,860✔
868
        return code;
93,860✔
869
      }
870
    }
871
  }
872

873
_end:
×
874
  if (code != TSDB_CODE_SUCCESS) {
×
875
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
876
    pTaskInfo->code = code;
×
877
    T_LONG_JMP(pTaskInfo->env, code);
×
878
  }
879
  return code;
×
880
}
881

882
int32_t getGroupSortExplainExecInfo(SOperatorInfo* pOptr, void** pOptrExplain, uint32_t* len) {
×
883
  SGroupSortOperatorInfo* pInfo = (SGroupSortOperatorInfo*)pOptr->info;
×
884
  *pOptrExplain = &pInfo->sortExecInfo;
×
885
  *len = sizeof(SSortExecInfo);
×
886
  return TSDB_CODE_SUCCESS;
×
887
}
888

889
void destroyGroupSortOperatorInfo(void* param) {
115,870✔
890
  SGroupSortOperatorInfo* pInfo = (SGroupSortOperatorInfo*)param;
115,870✔
891
  blockDataDestroy(pInfo->binfo.pRes);
115,870✔
892
  pInfo->binfo.pRes = NULL;
116,360✔
893

894
  taosArrayDestroy(pInfo->pSortInfo);
116,360✔
895
  taosArrayDestroy(pInfo->matchInfo.pList);
116,360✔
896

897
  tsortDestroySortHandle(pInfo->pCurrSortHandle);
116,360✔
898
  pInfo->pCurrSortHandle = NULL;
116,360✔
899

900
  taosMemoryFreeClear(param);
116,360✔
901
}
115,870✔
902

903
static int32_t resetGroupSortOperState(SOperatorInfo* pOper) {
×
904
  SGroupSortOperatorInfo* pInfo = pOper->info;
×
905
  SExecTaskInfo*           pTaskInfo = pOper->pTaskInfo;
×
906
  pOper->status = OP_NOT_OPENED;
×
907

908
  pInfo->currGroupId = 0;
×
909
  pInfo->hasGroupId = false;
×
910
  pInfo->prefetchedSortInput = NULL;
×
911
  pInfo->childOpStatus = CHILD_OP_NEW_GROUP;
×
912
  pInfo->sortExecInfo = (SSortExecInfo){0};
×
913
  
914
  resetBasicOperatorState(&pInfo->binfo);
×
915
  destroySqlFunctionCtx(pOper->exprSupp.pCtx, pOper->exprSupp.pExprInfo, pOper->exprSupp.numOfExprs);
×
916
  taosMemoryFreeClear(pOper->exprSupp.rowEntryInfoOffset);
×
917
  pOper->exprSupp.pCtx =
×
918
      createSqlFunctionCtx(pOper->exprSupp.pExprInfo, pOper->exprSupp.numOfExprs, &pOper->exprSupp.rowEntryInfoOffset, &pTaskInfo->storageAPI.functionStore);
×
919

920
  tsortDestroySortHandle(pInfo->pCurrSortHandle);
×
921
  pInfo->pCurrSortHandle = NULL;
×
922

923
  return 0;
×
924
}
925

926
int32_t createGroupSortOperatorInfo(SOperatorInfo* downstream, SGroupSortPhysiNode* pSortPhyNode,
114,701✔
927
                                    SExecTaskInfo* pTaskInfo, SOperatorInfo** pOptrInfo) {
928
  QRY_PARAM_CHECK(pOptrInfo);
114,701✔
929
  int32_t code = 0;
115,192✔
930
  int32_t lino = 0;
115,192✔
931

932
  SGroupSortOperatorInfo* pInfo = taosMemoryCalloc(1, sizeof(SGroupSortOperatorInfo));
115,192✔
933
  SOperatorInfo*          pOperator = taosMemoryCalloc(1, sizeof(SOperatorInfo));
113,722✔
934
  if (pInfo == NULL || pOperator == NULL) {
115,870✔
935
    code = terrno;
×
936
    goto _error;
×
937
  }
938
  initOperatorCostInfo(pOperator);
115,870✔
939

940
  SExprSupp*          pSup = &pOperator->exprSupp;
115,192✔
941
  SDataBlockDescNode* pDescNode = pSortPhyNode->node.pOutputDataBlockDesc;
116,360✔
942

943
  int32_t    numOfCols = 0;
115,870✔
944
  SExprInfo* pExprInfo = NULL;
116,360✔
945
  code = createExprInfo(pSortPhyNode->pExprs, NULL, &pExprInfo, &numOfCols);
116,360✔
946
  QUERY_CHECK_CODE(code, lino, _error);
114,212✔
947

948
  pSup->pExprInfo = pExprInfo;
114,212✔
949
  pSup->numOfExprs = numOfCols;
113,722✔
950

951
  initResultSizeInfo(&pOperator->resultInfo, 1024);
114,702✔
952
  pOperator->exprSupp.pCtx = createSqlFunctionCtx(pExprInfo, numOfCols, &pOperator->exprSupp.rowEntryInfoOffset,
115,870✔
953
                                                  &pTaskInfo->storageAPI.functionStore);
954
  QUERY_CHECK_NULL(pOperator->exprSupp.pCtx, code, lino, _error, terrno);
113,157✔
955

956
  pInfo->binfo.pRes = createDataBlockFromDescNode(pDescNode);
113,157✔
957
  QUERY_CHECK_NULL(pInfo->binfo.pRes, code, lino, _error, terrno);
116,360✔
958

959
  code = blockDataEnsureCapacity(pInfo->binfo.pRes, pOperator->resultInfo.capacity);
115,776✔
960
  TSDB_CHECK_CODE(code, lino, _error);
116,360✔
961

962
  pInfo->binfo.inputTsOrder = pSortPhyNode->node.inputTsOrder;
116,360✔
963
  pInfo->binfo.outputTsOrder = pSortPhyNode->node.outputTsOrder;
116,360✔
964

965
  int32_t numOfOutputCols = 0;
115,776✔
966
  code = extractColMatchInfo(pSortPhyNode->pTargets, pDescNode, &numOfOutputCols, COL_MATCH_FROM_SLOT_ID,
115,776✔
967
                             &pInfo->matchInfo);
968
  TSDB_CHECK_CODE(code, lino, _error);
115,211✔
969

970
  pInfo->pSortInfo = createSortInfo(pSortPhyNode->pSortKeys);
115,211✔
971
  setOperatorInfo(pOperator, "GroupSortOperator", QUERY_NODE_PHYSICAL_PLAN_GROUP_SORT, false, OP_NOT_OPENED, pInfo,
116,360✔
972
                  pTaskInfo);
973
  pOperator->fpSet = createOperatorFpSet(optrDummyOpenFn, doGroupSort, NULL, destroyGroupSortOperatorInfo,
114,702✔
974
                                         optrDefaultBufFn, getGroupSortExplainExecInfo, optrDefaultGetNextExtFn, NULL);
975

976
  setOperatorResetStateFn(pOperator, resetGroupSortOperState);
114,627✔
977
                                         
978
  code = appendDownstream(pOperator, &downstream, 1);
115,305✔
979
  if (code != TSDB_CODE_SUCCESS) {
115,870✔
980
    goto _error;
×
981
  }
982

983
  *pOptrInfo = pOperator;
115,870✔
984
  return TSDB_CODE_SUCCESS;
116,360✔
985

986
_error:
×
987
  pTaskInfo->code = code;
×
988
  if (pInfo != NULL) {
×
989
    destroyGroupSortOperatorInfo(pInfo);
×
990
  }
991
  destroyOperatorAndDownstreams(pOperator, &downstream, 1);
×
992
  return code;
×
993
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc