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

taosdata / TDengine / #5051

13 May 2026 12:00PM UTC coverage: 73.358% (-0.04%) from 73.398%
#5051

push

travis-ci

web-flow
feat: taosdump support stream backup/restore (#35326)

139 of 170 new or added lines in 3 files covered. (81.76%)

714 existing lines in 146 files now uncovered.

281543 of 383795 relevant lines covered (73.36%)

135448694.71 hits per line

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

74.36
/source/libs/executor/src/externalwindowoperator.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 "operator.h"
18
#include "querytask.h"
19
#include "tdatablock.h"
20
#include "stream.h"
21
#include "filter.h"
22
#include "cmdnodes.h"
23

24
#define EXT_WIN_RES_ROWS_ALLOC_SIZE 10
25
#define EXT_WIN_CALC_GROUP_SIZE 1000
26
#define EXT_WIN_TYPE_STR(_isMAExtW) ((_isMAExtW) ? "mergeAligned" : "")
27

28
typedef struct SBlockList {
29
  const SSDataBlock* pSrcBlock;
30
  SList*             pBlocks;
31
  int32_t            blockRowNumThreshold;
32
} SBlockList;
33

34

35
typedef int32_t (*extWinGetWinFp)(SOperatorInfo*, int64_t*, int32_t*, SDataBlockInfo*, SExtWinTimeWindow**, int32_t*);
36

37
typedef struct SExtWindowStat {
38
  int64_t resBlockCreated;
39
  int64_t resBlockDestroyed;
40
  int64_t resBlockRecycled;
41
  int64_t resBlockReused;
42
  int64_t resBlockAppend;
43
} SExtWindowStat;
44

45

46
typedef struct SExtWinCalcGrpCtx {
47
  uint64_t           groupId;
48
  SArray*            pWins;           // SArray<SExtWinTimeWindow>
49
  int32_t            curIdx; // for pesudo func calculation
50

51
  bool               blkWinStartSet;
52
  int32_t            blkWinStartIdx;
53
  int32_t            blkWinIdx;
54
  int32_t            blkRowStartIdx;
55

56
  SArray*            outWinBufIdx;
57
  int32_t            outWinTotalNum;      // agg: total output win num
58
  int32_t            outWinNum;           // already output win num
59
  int32_t            outWinIdx;           // current output win idx
60
  int32_t            outWinLastIdx;
61
  
62
  int32_t            lastWinIdx;
63
  int64_t            lastSKey;
64
  int64_t            lastEKey;
65
  int32_t            lastWinId;
66

67
  // Cached representative row with source data, used for group-key fallback in fill
68
  // paths. Set on first search, reused for all empty windows in this group.
69
  bool               anyRowCached;
70
  SResultRow*        pAnyRow;
71
} SExtWinCalcGrpCtx;
72

73
typedef struct SExtWinTrigGrpCtx {
74
  SExtWinCalcGrpCtx* pCCtx;
75
  int32_t            lastCtxIter;
76

77
  SSHashObj*         pCGCtxs; // calc groups ctxs, groupId => SExtWinCalcGrpCtx
78
} SExtWinTrigGrpCtx;
79

80

81
typedef struct SExtWinResultRows {
82
  int32_t             resRowsSize;
83
  int32_t             resRowsIdx;
84
  int32_t             resRowSize;
85
  int32_t             resRowIdx;
86
  int64_t             resRowAllcNum;
87
  SResultRow**        pResultRows;
88
} SExtWinResultRows;
89

90
typedef struct SExternalWindowOperator {
91
  SOptrBasicInfo     binfo;
92
  SExprSupp          scalarSupp;
93
  SExprSupp          projSupp;
94
  int32_t            primaryTsIndex;
95
  EExtWinMode        mode;
96
  EFillMode          fillMode;
97
  bool               multiTableMode;
98
  bool               inputHasOrder;
99
  bool               needGroupSort;
100
  bool               extWinSplit;
101
  bool               calcWithPartition;
102
  SArray*            pPseudoColInfo;  
103
  STimeRangeNode*    timeRangeExpr;
104
  
105
  extWinGetWinFp     getWinFp;
106

107
  uint64_t           lastTGrpId;
108
  uint64_t           lastCGrpId;
109
  SExtWinTrigGrpCtx* pTGrpCtx;
110
  bool               ownTGrpCtx;
111
  SArray*            pGrpIds;       // single calc or trig group ids
112
  SArray*            pCTGrpIds;
113
  
114
  // for project&indefRows
115
  SList*             pFreeBlocks;    // SList<SSDatablock*+SAarray*>
116
  SArray*            pOutputBlocks;  // SArray<SList*>, for each window, we have a list of blocks
117
  SListNode*         pLastBlkNode; 
118
  SSDataBlock*       pTmpBlock;
119
  SSDataBlock*       pProjTmpBlock;  // reusable tmp block for extWinApplyAggPostProjection
120
  
121
  // for agg
122
  SAggSupporter      aggSup;
123
  STimeWindowAggSupp twAggSup;
124

125
  SExtWinResultRows  resultRows;
126

127
  // for output
128
  SSTriggerGroupCalcInfo* pLastOutput;
129
  int32_t                 lastOutputIter;
130
  int32_t                 lastGrpIdx;    // index in pGrpIds or pCTGrpIds
131

132
  int32_t            outWinIdx;
133
  int32_t            resWinIdx;        // for result win allocation
134
  SSDataBlock*       pEmptyInputBlock;
135
  bool               hasCountFunc;
136
  SNodeList*         pFillExprs;
137
  SNode*             pFillValues;
138
  SColMatchInfo      fillMatchInfo;
139
  char*              pFillRowBuf;    // reusable tmp buffer for extWinAppendAggFilledRow
140
  SExtWindowStat     stat;
141
  SArray*            pWinRowIdx;
142
  bool               isMergeAlignedExtW;
143

144
  // for vtable window query
145
  bool               isDynWindow;
146
  int32_t            orgTableVgId;
147
  tb_uid_t           orgTableUid;
148
  STimeWindow        orgTableTimeRange;
149

150
  SExecTaskInfo*     pTaskInfo;  // for pRunnerGrpCtx cleanup in destroy
151
} SExternalWindowOperator;
152

153
static char* extWinModeStr(EExtWinMode mode) {
4,624,083✔
154
  switch (mode) {
4,624,083✔
155
    case EEXT_MODE_SCALAR:
101,460✔
156
      return "scalar";
101,460✔
157
    case EEXT_MODE_AGG:
4,522,623✔
158
      return "agg";
4,522,623✔
159
    case EEXT_MODE_INDEFR_FUNC:
×
160
      return "indefRows";
×
161
    default:
×
162
      break;
×
163
  }
164

165
  return "unknown";
×
166
}
167

168

169
static int extWinGrpIdCompare(const void* p1, const void* p2) {
890✔
170
  uint64_t* gId1 = (uint64_t*)p1;
890✔
171
  uint64_t* gId2 = (uint64_t*)p2;
890✔
172

173
  if (*gId1 == *gId2) {
890✔
174
    return 0;
×
175
  }
176
  
177
  return (*gId1) < (*gId2) ? -1 : 1;
890✔
178
}
179

180
static void extWinDestroyCGrpCtx(void* param) {
1,644,236✔
181
  if (NULL == param) {
1,644,236✔
182
    return;
×
183
  }
184

185
  SExtWinCalcGrpCtx* pCtx = (SExtWinCalcGrpCtx*)param;
1,644,236✔
186

187
  taosArrayDestroy(pCtx->pWins);
1,644,236✔
188
}
189

190
static void extWinDestroyTGrpCtx(void* param) {
1,491,969✔
191
  if (NULL == param) {
1,491,969✔
192
    return;
×
193
  }
194

195
  SExtWinTrigGrpCtx* pCtx = (SExtWinTrigGrpCtx*)param;
1,491,969✔
196

197
  if (pCtx->pCGCtxs) {
1,491,969✔
198
    tSimpleHashSetFreeFp(pCtx->pCGCtxs, extWinDestroyCGrpCtx);
115,805✔
199
    tSimpleHashCleanup(pCtx->pCGCtxs);
115,805✔
200
  } else if (pCtx->pCCtx) {
1,376,164✔
201
    extWinDestroyCGrpCtx(pCtx->pCCtx);
1,372,576✔
202
    taosMemoryFree(pCtx->pCCtx);
1,372,576✔
203
  }
204
}
205

206
static int32_t extWinBlockListAddBlock(SExternalWindowOperator* pExtW, SList* pList, int32_t rows, SSDataBlock** ppBlock, SArray** ppIdx) {
4,509,630✔
207
  SSDataBlock* pRes = NULL;
4,509,630✔
208
  int32_t code = 0, lino = 0;
4,509,630✔
209

210
  if (listNEles(pExtW->pFreeBlocks) > 0) {
4,509,630✔
211
    SListNode* pNode = tdListPopHead(pExtW->pFreeBlocks);
×
212
    *ppBlock = *(SSDataBlock**)pNode->data;
×
213
    *ppIdx = *(SArray**)((SArray**)pNode->data + 1);
×
214
    tdListAppendNode(pList, pNode);
×
215
    pExtW->stat.resBlockReused++;
×
216
  } else {
217
    TAOS_CHECK_EXIT(createOneDataBlock(pExtW->binfo.pRes, false, &pRes));
4,509,630✔
218
    TAOS_CHECK_EXIT(blockDataEnsureCapacity(pRes, TMAX(rows, 4096)));
4,509,630✔
219
    SArray* pIdx = taosArrayInit(10, sizeof(int64_t));
4,509,630✔
220
    TSDB_CHECK_NULL(pIdx, code, lino, _exit, terrno);
4,509,630✔
221
    void* res[2] = {pRes, pIdx};
4,509,630✔
222
    TAOS_CHECK_EXIT(tdListAppend(pList, res));
4,509,630✔
223

224
    *ppBlock = pRes;
4,509,185✔
225
    *ppIdx = pIdx;
4,508,740✔
226
    pExtW->stat.resBlockCreated++;
4,509,630✔
227
  }
228

229
  (*ppBlock)->info.id.groupId = pExtW->lastCGrpId;
4,509,630✔
230
  // ensure projection path blocks also carry the matching T-group id
231
  (*ppBlock)->info.id.baseGId = pExtW->lastTGrpId;
4,509,630✔
232

233
  
234
_exit:
4,509,630✔
235

236
  if (code) {
4,509,630✔
237
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
238
    blockDataDestroy(pRes);
×
239
  }
240
  
241
  return code;
4,509,630✔
242
}
243

244
static void extWinPostUpdateStreamRt(SStreamRuntimeFuncInfo* pStream, SOperatorInfo* pOperator, SExternalWindowOperator* pExtW) {
4,632,895✔
245
  if (pStream->curGrpCalc) {
4,632,895✔
246
    pStream->createTable = &pStream->curGrpCalc->createTable;
4,490,940✔
247
    pStream->pStreamPesudoFuncVals = pStream->curGrpCalc->pParams;
4,490,940✔
248
    pStream->pStreamPartColVals = pStream->curGrpCalc->pGroupColVals;
4,490,940✔
249
  }
250
  
251
  pStream->groupId = pExtW->lastTGrpId;
4,632,895✔
252

253
  qDebug("%s streamRt updated, groupId %" PRIu64, GET_TASKID(pOperator->pTaskInfo), pStream->groupId);
4,632,895✔
254
}
4,633,785✔
255

256
static void extWinAssignBlockGrpId(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW, SBlockID* pId) {
8,607,580✔
257
  uint64_t currentCGrpId = (pExtW->pTGrpCtx != NULL && pExtW->pTGrpCtx->pCCtx != NULL)
17,003,340✔
258
                               ? pExtW->pTGrpCtx->pCCtx->groupId
8,321,312✔
259
                               : pExtW->lastCGrpId;
17,003,340✔
260

261
  if (pExtW->calcWithPartition && !pOperator->pTaskInfo->pStreamRuntimeInfo->funcInfo.isMultiGroupCalc) {
8,607,580✔
262
    // For partitioned non-multi-group output, the effective merge group is the
263
    // current calc-group id. If we keep using lastTGrpId here, all output
264
    // blocks collapse to group 0 and the downstream merge path may concatenate
265
    // different partition groups without re-ordering by _group_id.
266
    pId->groupId = currentCGrpId;
4,692,840✔
267
    pId->baseGId = 0;
4,692,840✔
268
  } else if (pExtW->extWinSplit) {
3,914,740✔
269
    pId->groupId = currentCGrpId;
315,060✔
270
    pId->baseGId = pExtW->lastTGrpId;
315,060✔
271
  } else {
272
    pId->groupId = pExtW->lastTGrpId;
3,599,680✔
273
    pId->baseGId = 0;
3,599,680✔
274
  }
275

276
  qDebug("%s extWin res block assigned groupId %" PRIu64 " baseGid %" PRIu64, GET_TASKID(pOperator->pTaskInfo), pId->groupId, pId->baseGId);
8,607,580✔
277
}
8,607,580✔
278

279
static FORCE_INLINE void extWinResetBlockCalcState(SExtWinCalcGrpCtx* pCCtx) {
280
  if (pCCtx == NULL) {
4,096,240✔
281
    return;
×
282
  }
283

284
  pCCtx->blkWinIdx = -1;
4,096,240✔
285
  pCCtx->blkWinStartSet = false;
4,096,240✔
286
  pCCtx->blkRowStartIdx = 0;
4,096,240✔
287
}
288

289
static int32_t extWinGetLastBlockFromList(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW, SList* pList, int32_t rows, SSDataBlock** ppBlock, SArray** ppIdx) {
11,217,115✔
290
  int32_t    code = 0, lino = 0;
11,217,115✔
291
  SSDataBlock* pRes = NULL;
11,217,115✔
292

293
  SListNode* pNode = TD_DLIST_TAIL(pList);
11,217,115✔
294
  if (NULL == pNode) {
11,217,115✔
295
    TAOS_CHECK_EXIT(extWinBlockListAddBlock(pExtW, pList, rows, ppBlock, ppIdx));
4,508,295✔
296
    return code;
4,507,850✔
297
  }
298

299
  pRes = *(SSDataBlock**)pNode->data;
6,708,820✔
300
  if ((pRes->info.rows + rows) > pRes->info.capacity) {
6,708,820✔
301
    TAOS_CHECK_EXIT(extWinBlockListAddBlock(pExtW, pList, rows, ppBlock, ppIdx));
1,335✔
302
    return code;
1,335✔
303
  }
304

305
  extWinAssignBlockGrpId(pOperator, pExtW, &pRes->info.id);
6,707,485✔
306
  
307
  *ppIdx = *(SArray**)((SSDataBlock**)pNode->data + 1);
6,707,485✔
308
  *ppBlock = pRes;
6,707,485✔
309
  pExtW->stat.resBlockAppend++;
6,707,485✔
310

311
_exit:
6,707,485✔
312

313
  if (code) {
6,707,485✔
314
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
315
  }
316
  
317
  return code;
6,707,485✔
318
}
319

320
static void extWinDestroyBlockList(void* p) {
4,508,295✔
321
  if (NULL == p) {
4,508,295✔
322
    return;
×
323
  }
324

325
  SListNode* pTmp = NULL;
4,508,295✔
326
  SList** ppList = (SList**)p;
4,508,295✔
327
  if ((*ppList) && TD_DLIST_NELES(*ppList) > 0) {
4,508,295✔
328
    SListNode* pNode = TD_DLIST_HEAD(*ppList);
890✔
329
    while (pNode) {
1,780✔
330
      SSDataBlock* pBlock = *(SSDataBlock**)pNode->data;
890✔
331
      blockDataDestroy(pBlock);
890✔
332
      SArray* pIdx = *(SArray**)((SArray**)pNode->data + 1);
890✔
333
      taosArrayDestroy(pIdx);
890✔
334
      pTmp = pNode;
890✔
335
      pNode = pNode->dl_next_;
890✔
336
      taosMemoryFree(pTmp);
890✔
337
    }
338
  }
339
  taosMemoryFree(*ppList);
4,508,295✔
340
}
341

342

343
static void extWinRecycleBlkNode(SExternalWindowOperator* pExtW, SListNode** ppNode) {
6,466,253✔
344
  if (NULL == ppNode || NULL == *ppNode) {
6,466,253✔
345
    return;
1,960,628✔
346
  }
347

348
  SSDataBlock* pBlock = *(SSDataBlock**)(*ppNode)->data;
4,506,070✔
349
  SArray* pIdx = *(SArray**)((SArray**)(*ppNode)->data + 1);
4,506,515✔
350
  
351
  if (listNEles(pExtW->pFreeBlocks) >= 10) {
4,506,515✔
352
    blockDataDestroy(pBlock);
4,439,765✔
353
    taosArrayDestroy(pIdx);
4,440,655✔
354
    taosMemoryFreeClear(*ppNode);
4,441,100✔
355
    pExtW->stat.resBlockDestroyed++;
4,441,100✔
356
    return;
4,441,100✔
357
  }
358
  
359
  blockDataCleanup(pBlock);
66,750✔
360
  taosArrayClear(pIdx);
66,750✔
361
  tdListPrependNode(pExtW->pFreeBlocks, *ppNode);
66,750✔
362
  *ppNode = NULL;
66,750✔
363
  pExtW->stat.resBlockRecycled++;
66,750✔
364
}
365

366
static void extWinRecycleBlockList(SExternalWindowOperator* pExtW, void* p) {
×
367
  if (NULL == p) {
×
368
    return;
×
369
  }
370

371
  SListNode* pTmp = NULL;
×
372
  SList** ppList = (SList**)p;
×
373
  if ((*ppList) && TD_DLIST_NELES(*ppList) > 0) {
×
374
    SListNode* pNode = TD_DLIST_HEAD(*ppList);
×
375
    while (pNode) {
×
376
      pTmp = pNode;
×
377
      pNode = pNode->dl_next_;
×
378
      extWinRecycleBlkNode(pExtW, &pTmp);
×
379
    }
380
  }
381
  taosMemoryFree(*ppList);
×
382
  *ppList = NULL;
×
383
}
384
static void extWinDestroyBlkNode(SExternalWindowOperator* pInfo, SListNode* pNode) {
1,798,160✔
385
  if (NULL == pNode) {
1,798,160✔
386
    return;
1,730,520✔
387
  }
388

389
  SSDataBlock* pBlock = *(SSDataBlock**)pNode->data;
67,640✔
390
  SArray* pIdx = *(SArray**)((SArray**)pNode->data + 1);
67,640✔
391
  
392
  blockDataDestroy(pBlock);
67,640✔
393
  taosArrayDestroy(pIdx);
67,640✔
394

395
  taosMemoryFree(pNode);
67,640✔
396

397
  pInfo->stat.resBlockDestroyed++;
67,640✔
398
}
399

400

401
static void destroyExternalWindowOperatorInfo(void* param) {
1,731,410✔
402
  if (NULL == param) {
1,731,410✔
403
    return;
×
404
  }
405
  SExternalWindowOperator* pInfo = (SExternalWindowOperator*)param;
1,731,410✔
406
  cleanupBasicInfo(&pInfo->binfo);
1,731,410✔
407

408
  taosArrayDestroyEx(pInfo->pOutputBlocks, extWinDestroyBlockList);
1,731,410✔
409
  colDataDestroy(&pInfo->twAggSup.timeWindowData);
1,731,410✔
410
  taosArrayDestroy(pInfo->pWinRowIdx);
1,731,410✔
411
  
412
  taosArrayDestroy(pInfo->pPseudoColInfo);
1,731,410✔
413
  taosArrayDestroy(pInfo->fillMatchInfo.pList);
1,731,410✔
414
  blockDataDestroy(pInfo->pTmpBlock);
1,731,410✔
415
  blockDataDestroy(pInfo->pProjTmpBlock);
1,730,965✔
416
  blockDataDestroy(pInfo->pEmptyInputBlock);
1,730,965✔
417
  taosMemoryFree(pInfo->pFillRowBuf);
1,730,965✔
418

419
  extWinDestroyBlkNode(pInfo, pInfo->pLastBlkNode);
1,730,965✔
420
  if (pInfo->pFreeBlocks) {
1,731,410✔
421
    SListNode *node;
422
    while ((node = TD_DLIST_HEAD(pInfo->pFreeBlocks)) != NULL) {
125,935✔
423
      TD_DLIST_POP(pInfo->pFreeBlocks, node);
66,750✔
424
      extWinDestroyBlkNode(pInfo, node);
66,750✔
425
    }
426
    taosMemoryFree(pInfo->pFreeBlocks);
59,185✔
427
  }
428

429
  taosArrayDestroy(pInfo->pGrpIds);
1,731,410✔
430
  taosArrayDestroy(pInfo->pCTGrpIds);
1,730,520✔
431

432
  if (pInfo->ownTGrpCtx && pInfo->pTGrpCtx) {
1,730,965✔
433
    extWinDestroyTGrpCtx(pInfo->pTGrpCtx);
1,458,594✔
434
    taosMemoryFree(pInfo->pTGrpCtx);
1,458,594✔
435
    pInfo->pTGrpCtx = NULL;
1,458,594✔
436
  }
437

438
  // Free executor-specific pRunnerGrpCtx stored inside grouped calc info entries.
439
  // These contain SExtWinTrigGrpCtx which must be cleaned up here (before
440
  // doDestroyTask calls tDestroyStRtFuncInfo) since extWinDestroyTGrpCtx is
441
  // only accessible from this translation unit.
442
  if (pInfo->pTaskInfo != NULL && pInfo->pTaskInfo->pStreamRuntimeInfo != NULL) {
1,730,965✔
443
    SStreamRuntimeFuncInfo* pRt = &pInfo->pTaskInfo->pStreamRuntimeInfo->funcInfo;
564,333✔
444
    if (pRt->pGroupCalcInfos != NULL) {
563,888✔
445
      int32_t iter = 0;
76,985✔
446
      void*   pIter = NULL;
76,985✔
447
      while ((pIter = tSimpleHashIterate(pRt->pGroupCalcInfos, pIter, &iter)) != NULL) {
230,510✔
448
        SSTriggerGroupCalcInfo* pGrpCalc = (SSTriggerGroupCalcInfo*)pIter;
153,525✔
449
        if (pGrpCalc->pRunnerGrpCtx != NULL) {
153,525✔
450
          extWinDestroyTGrpCtx(pGrpCalc->pRunnerGrpCtx);
33,375✔
451
          taosMemoryFree(pGrpCalc->pRunnerGrpCtx);
33,375✔
452
          pGrpCalc->pRunnerGrpCtx = NULL;
33,375✔
453
        }
454
      }
455
    }
456
  }
457

458
  cleanupAggSup(&pInfo->aggSup);
1,731,855✔
459
  cleanupExprSupp(&pInfo->scalarSupp);
1,730,965✔
460
  cleanupExprSupp(&pInfo->projSupp);
1,731,410✔
461
  for (int32_t i = 0; i < pInfo->resultRows.resRowsSize; ++i) {
18,401,150✔
462
    if (pInfo->resultRows.pResultRows && pInfo->resultRows.pResultRows[i]) {
16,670,185✔
463
      taosMemoryFreeClear(pInfo->resultRows.pResultRows[i]);
1,403,120✔
464
    }
465
  }
466
  taosMemoryFreeClear(pInfo->resultRows.pResultRows);
1,731,410✔
467
  
468
  pInfo->binfo.resultRowInfo.openWindow = tdListFree(pInfo->binfo.resultRowInfo.openWindow);
1,730,965✔
469

470
  qDebug("ext window stat at destroy, created:%" PRId64 ", destroyed:%" PRId64 ", recycled:%" PRId64 ", reused:%" PRId64 ", append:%" PRId64, 
1,731,410✔
471
      pInfo->stat.resBlockCreated, pInfo->stat.resBlockDestroyed, pInfo->stat.resBlockRecycled, 
472
      pInfo->stat.resBlockReused, pInfo->stat.resBlockAppend);
473

474
  taosMemoryFreeClear(pInfo);
1,731,410✔
475
}
476

477
static int32_t extWinOpen(SOperatorInfo* pOperator);
478
static int32_t extWinNext(SOperatorInfo* pOperator, SSDataBlock** ppRes);
479
static int32_t mergeAlignExtWinNext(SOperatorInfo* pOperator, SSDataBlock** ppRes);
480

481
static FORCE_INLINE void extWinIntersectTimeRange(STimeWindow* pDst, const STimeWindow* pRange) {
482
  if (pDst == NULL || pRange == NULL) {
971,136✔
483
    return;
×
484
  }
485

486
  pDst->skey = TMAX(pDst->skey, pRange->skey);
971,136✔
487
  pDst->ekey = TMIN(pDst->ekey, pRange->ekey);
971,136✔
488
}
489

490
static void extWinApplyTimeRangeToTableScan(SOperatorInfo* pScanOp, const STimeWindow* pTimeRange) {
485,568✔
491
  if (pScanOp == NULL || pScanOp->info == NULL || pTimeRange == NULL) {
485,568✔
492
    return;
×
493
  }
494

495
  STableScanInfo* pScanInfo = (STableScanInfo*)pScanOp->info;
485,568✔
496
  extWinIntersectTimeRange(&pScanInfo->base.cond.twindows, pTimeRange);
485,568✔
497
  extWinIntersectTimeRange(&pScanInfo->base.orgCond.twindows, pTimeRange);
485,568✔
498
}
499

500
static void extWinApplyTimeRangeToExchangeParam(SOperatorParam* pParam, const STimeWindow* pTimeRange) {
29,815✔
501
  if (pParam == NULL || pParam->value == NULL || pTimeRange == NULL) {
29,815✔
502
    return;
29,815✔
503
  }
504

505
  SExchangeOperatorParam* pExcParam = (SExchangeOperatorParam*)pParam->value;
×
506
  if (pExcParam->multiParams) {
×
507
    return;
×
508
  }
509

510
  if (pExcParam->basic.paramType == 0) {
×
511
    pExcParam->basic.paramType = DYN_TYPE_EXCHANGE_PARAM;
×
512
  }
513
  extWinIntersectTimeRange(&pExcParam->basic.window, pTimeRange);
×
514
}
515

516
static int32_t extWinApplyNonStreamTimeRangeToOperatorTree(SOperatorInfo* pOperator, const STimeWindow* pTimeRange,
545,198✔
517
                                                           int32_t* pScanAppliedNum, SExecTaskInfo* pRootTaskInfo) {
518
  if (pOperator == NULL || pTimeRange == NULL || pScanAppliedNum == NULL || pRootTaskInfo == NULL) {
545,198✔
519
    return TSDB_CODE_INVALID_PARA;
×
520
  }
521

522
  if (pOperator->pTaskInfo != pRootTaskInfo) {
545,198✔
523
    return TSDB_CODE_SUCCESS;
×
524
  }
525

526
  if (pOperator->operatorType == QUERY_NODE_PHYSICAL_PLAN_TABLE_SCAN) {
545,198✔
527
    extWinApplyTimeRangeToTableScan(pOperator, pTimeRange);
485,568✔
528
    ++(*pScanAppliedNum);
485,568✔
529
  }
530

531
  if (pOperator->operatorType == QUERY_NODE_PHYSICAL_PLAN_EXCHANGE) {
544,753✔
532
    extWinApplyTimeRangeToExchangeParam(pOperator->pOperatorGetParam, pTimeRange);
29,815✔
533
  }
534

535
  if (pOperator->numOfDownstream <= 0 || pOperator->pDownstream == NULL) {
544,753✔
536
    return TSDB_CODE_SUCCESS;
515,383✔
537
  }
538

539
  for (int32_t i = 0; i < pOperator->numOfDownstream; ++i) {
76,985✔
540
    SOperatorInfo* pChild = pOperator->pDownstream[i];
47,170✔
541
    if (pChild == NULL) {
47,170✔
542
      continue;
×
543
    }
544

545
    if (pOperator->pDownstreamGetParams != NULL &&
47,170✔
546
        pChild->operatorType == QUERY_NODE_PHYSICAL_PLAN_EXCHANGE) {
×
547
      extWinApplyTimeRangeToExchangeParam(pOperator->pDownstreamGetParams[i], pTimeRange);
×
548
    }
549

550
    int32_t code = extWinApplyNonStreamTimeRangeToOperatorTree(pChild, pTimeRange, pScanAppliedNum, pRootTaskInfo);
47,170✔
551
    if (code != TSDB_CODE_SUCCESS) {
47,170✔
552
      return code;
×
553
    }
554
  }
555

556
  return TSDB_CODE_SUCCESS;
29,815✔
557
}
558

559
static int32_t extWinApplyNonStreamTimeRangeToDownstream(SOperatorInfo* pOperator, const STimeWindow* pTimeRange) {
1,667,330✔
560
  if (pOperator == NULL || pTimeRange == NULL) {
1,667,330✔
561
    return TSDB_CODE_INVALID_PARA;
×
562
  }
563

564
  if (pTimeRange->skey == INT64_MAX || pTimeRange->ekey == INT64_MIN || pTimeRange->skey > pTimeRange->ekey) {
1,667,330✔
565
    return TSDB_CODE_SUCCESS;
1,169,302✔
566
  }
567

568
  if (pOperator->numOfDownstream <= 0 || pOperator->pDownstream == NULL) {
498,028✔
569
    return TSDB_CODE_SUCCESS;
×
570
  }
571

572
  int32_t scanAppliedNum = 0;
498,028✔
573
  for (int32_t i = 0; i < pOperator->numOfDownstream; ++i) {
996,056✔
574
    SOperatorInfo* pDownstream = pOperator->pDownstream[i];
498,028✔
575
    if (pDownstream == NULL) {
498,028✔
576
      continue;
×
577
    }
578

579
    int32_t code = extWinApplyNonStreamTimeRangeToOperatorTree(pDownstream, pTimeRange, &scanAppliedNum,
498,028✔
580
                                                                pOperator->pTaskInfo);
581
    if (code != TSDB_CODE_SUCCESS) {
498,028✔
582
      return code;
×
583
    }
584
  }
585

586
  qInfo("%s apply non-stream extWin timerange:[%" PRId64 ", %" PRId64 "] to downstream tree, tableScanCnt:%d",
498,028✔
587
        GET_TASKID(pOperator->pTaskInfo), pTimeRange->skey, pTimeRange->ekey, scanAppliedNum);
588

589
  return TSDB_CODE_SUCCESS;
498,028✔
590
}
591

592
typedef struct SMergeAlignedExternalWindowOperator {
593
  SExternalWindowOperator* pExtW;
594
  int64_t curTs;
595
  SResultRow*  pResultRow;
596
  SSDataBlock* pNewGroup;
597
  int32_t lastFinalizedWinIdx;  // tracks last emitted window index for filling empty-window gaps (vtable COLS merge)
598
} SMergeAlignedExternalWindowOperator;
599

600
static int32_t extWinInitNonStreamWindowDataFromBlock(SExternalWindowPhysiNode* pPhynode, SExecTaskInfo* pTaskInfo,
601
                                                       STimeWindow* pTimeRange);
602

603
static void destroyMergeAlignedExternalWindowOperator(void* pOperator) {
63,190✔
604
  SMergeAlignedExternalWindowOperator* pMAExtW = (SMergeAlignedExternalWindowOperator*)pOperator;
63,190✔
605
  destroyExternalWindowOperatorInfo(pMAExtW->pExtW);
63,190✔
606
  taosMemoryFreeClear(pMAExtW);
63,190✔
607
}
63,190✔
608

609
static int64_t* extWinExtractTsCol(SSDataBlock* pBlock, int32_t primaryTsIndex, SExecTaskInfo* pTaskInfo) {
4,171,445✔
610
  TSKEY* tsCols = NULL;
4,171,445✔
611

612
  if (pBlock->pDataBlock != NULL && pBlock->info.dataLoad) {
4,171,445✔
613
    SColumnInfoData* pColDataInfo = taosArrayGet(pBlock->pDataBlock, primaryTsIndex);
4,171,445✔
614
    if (!pColDataInfo) {
4,171,445✔
615
      pTaskInfo->code = terrno;
×
616
      T_LONG_JMP(pTaskInfo->env, terrno);
×
617
    }
618

619
    tsCols = (int64_t*)pColDataInfo->pData;
4,171,445✔
620
    if (pBlock->info.window.skey == 0 && pBlock->info.window.ekey == 0) {
4,171,445✔
621
      int32_t code = blockDataUpdateTsWindow(pBlock, primaryTsIndex);
3,783,805✔
622
      if (code != TSDB_CODE_SUCCESS) {
3,783,805✔
623
        qError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(code));
×
624
        pTaskInfo->code = code;
×
625
        T_LONG_JMP(pTaskInfo->env, code);
×
626
      }
627
    }
628
  }
629

630
  return tsCols;
4,171,445✔
631
}
632

633
static FORCE_INLINE SExternalWindowOperator* extWinGetCoreInfo(SOperatorInfo* pOperator) {
634
  if (pOperator == NULL) {
85,389,138✔
635
    return NULL;
×
636
  }
637

638
  // getNextFn is an execution-record wrapper; _nextFn is the real operator callback.
639
  if (pOperator->fpSet._nextFn == mergeAlignExtWinNext) {
85,389,138✔
640
    SMergeAlignedExternalWindowOperator* pMAExtW = pOperator->info;
9,970,225✔
641
    return (pMAExtW != NULL) ? pMAExtW->pExtW : NULL;
9,970,225✔
642
  }
643

644
  return pOperator->info;
75,418,023✔
645
}
646

647
static FORCE_INLINE SExtWinCalcGrpCtx* extWinGetScopedCalcGrpCtx(SOperatorInfo* pOperator) {
648
  SExternalWindowOperator* pExtW = extWinGetCoreInfo(pOperator);
85,389,138✔
649
  if (pExtW == NULL || pExtW->pTGrpCtx == NULL || pExtW->pTGrpCtx->pCCtx == NULL) {
85,389,138✔
650
    return NULL;
189,125✔
651
  }
652

653
  // In partitioned external-window queries, different calc-groups may share
654
  // the same trigger-group. Their window cursors must stay isolated per
655
  // calc-group instead of falling back to the task-global cursor.
656
  if (pExtW->calcWithPartition) {
85,201,348✔
657
    return pExtW->pTGrpCtx->pCCtx;
46,378,308✔
658
  }
659

660
  SExecTaskInfo* pTaskInfo = pOperator->pTaskInfo;
38,823,040✔
661
  if (pTaskInfo != NULL && pTaskInfo->pStreamRuntimeInfo != NULL) {
38,822,595✔
662
    SStreamRuntimeFuncInfo* pInfo = &pTaskInfo->pStreamRuntimeInfo->funcInfo;
38,823,485✔
663
    if (pInfo->isMultiGroupCalc) {
38,823,485✔
664
      return pExtW->pTGrpCtx->pCCtx;
×
665
    }
666
  }
667

668
  return NULL;
38,823,040✔
669
}
670

671
static int32_t extWinGetCurWinIdx(SOperatorInfo* pOperator) {
2,147,483,647✔
672
  SExecTaskInfo* pTaskInfo = pOperator->pTaskInfo;
2,147,483,647✔
673
  if (!pTaskInfo->pStreamRuntimeInfo) {
2,147,483,647✔
674
    return 0;
2,147,483,647✔
675
  }
676
  SExtWinCalcGrpCtx* pCCtx = extWinGetScopedCalcGrpCtx(pOperator);
47,867,977✔
677
  if (pCCtx != NULL) {
47,867,977✔
678
    return pCCtx->curIdx;
23,406,764✔
679
  }
680

681
  return pTaskInfo->pStreamRuntimeInfo->funcInfo.curIdx;
24,461,213✔
682
}
683

684
static void extWinSetCurWinIdx(SOperatorInfo* pOperator, int32_t idx) {
2,147,483,647✔
685
  SExecTaskInfo* pTaskInfo = pOperator->pTaskInfo;
2,147,483,647✔
686
  if (pTaskInfo->pStreamRuntimeInfo) {
2,147,483,647✔
687
    SExtWinCalcGrpCtx* pCCtx = extWinGetScopedCalcGrpCtx(pOperator);
33,008,416✔
688
    if (pCCtx != NULL) {
33,008,416✔
689
      pCCtx->curIdx = idx;
18,503,744✔
690
    }
691
    pTaskInfo->pStreamRuntimeInfo->funcInfo.curIdx = idx;
33,008,416✔
692
  }
693
}
2,147,483,647✔
694

695

696
static void extWinIncCurWinOutIdx(SOperatorInfo* pOperator) {
4,506,960✔
697
  if (pOperator == NULL || pOperator->pTaskInfo->pStreamRuntimeInfo == NULL) {
4,506,960✔
698
    return;
×
699
  }
700

701
  SExtWinCalcGrpCtx* pCCtx = extWinGetScopedCalcGrpCtx(pOperator);
4,507,405✔
702
  if (pCCtx != NULL) {
4,507,405✔
703
    pCCtx->curIdx++;
4,462,015✔
704
  }
705
  pOperator->pTaskInfo->pStreamRuntimeInfo->funcInfo.curIdx++;
4,507,850✔
706
}
707

708

709
static int32_t extWinAppendWinIdx(SExecTaskInfo*       pTaskInfo, SArray* pIdx, SSDataBlock* pBlock, int32_t currWinIdx, int32_t rows) {
2,147,483,647✔
710
  int32_t  code = 0, lino = 0;
2,147,483,647✔
711
  int64_t* lastRes = taosArrayGetLast(pIdx);
2,147,483,647✔
712
  int32_t* lastWinIdx = (int32_t*)lastRes;
2,147,483,647✔
713
  int32_t* lastRowIdx = lastWinIdx ? (lastWinIdx + 1) : NULL;
2,147,483,647✔
714
  int64_t  res = 0;
2,147,483,647✔
715
  int32_t* pWinIdx = (int32_t*)&res;
2,147,483,647✔
716
  int32_t* pRowIdx = pWinIdx + 1;
2,147,483,647✔
717

718
  if (lastWinIdx && *lastWinIdx == currWinIdx) {
2,147,483,647✔
719
    return code;
2,147,483,647✔
720
  }
721

722
  *pWinIdx = currWinIdx;
636,903,312✔
723
  *pRowIdx = pBlock->info.rows - rows;
636,894,857✔
724

725
  TSDB_CHECK_NULL(taosArrayPush(pIdx, &res), code, lino, _exit, terrno);
636,901,087✔
726

727
_exit:
636,901,087✔
728

729
  if (code) {
636,893,077✔
730
    qError("%s %s failed at line %d since %s", pTaskInfo->id.str, __func__, lino, tstrerror(code));
×
731
  }
732

733
  return code;
636,872,162✔
734
}
735

736
static int32_t extWinRebuildWinIdxByFilter(SExecTaskInfo* pTaskInfo, SArray* pIdx, int32_t rowsBeforeFilter,
8,546✔
737
                                           SColumnInfoData* pFilterRes) {
738
  int32_t code = TSDB_CODE_SUCCESS;
8,546✔
739
  int32_t lino = 0;
8,546✔
740

741
  if (pIdx == NULL || pFilterRes == NULL || rowsBeforeFilter <= 0 || taosArrayGetSize(pIdx) == 0) {
8,546✔
742
    return code;
×
743
  }
744

745
  int32_t idxSize = (int32_t)taosArrayGetSize(pIdx);
8,546✔
746
  SArray* pNewIdx = taosArrayInit(idxSize, sizeof(int64_t));
8,546✔
747
  TSDB_CHECK_NULL(pNewIdx, code, lino, _exit, terrno);
8,546✔
748

749
  int8_t* pIndicator = (int8_t*)pFilterRes->pData;
8,546✔
750
  int32_t newRowStart = 0;
8,546✔
751
  for (int32_t i = 0; i < idxSize; ++i) {
48,606✔
752
    int64_t cur = *(int64_t*)taosArrayGet(pIdx, i);
40,060✔
753
    int32_t* pCurWinIdx = (int32_t*)&cur;
40,060✔
754
    int32_t* pCurRowIdx = pCurWinIdx + 1;
40,060✔
755

756
    int32_t startRow = *pCurRowIdx;
40,060✔
757
    int32_t endRow = rowsBeforeFilter;
40,060✔
758
    if (i + 1 < idxSize) {
40,060✔
759
      int64_t next = *(int64_t*)taosArrayGet(pIdx, i + 1);
31,514✔
760
      int32_t* pNextWinIdx = (int32_t*)&next;
31,514✔
761
      int32_t* pNextRowIdx = pNextWinIdx + 1;
31,514✔
762
      endRow = *pNextRowIdx;
31,514✔
763
    }
764

765
    startRow = TMIN(TMAX(startRow, 0), rowsBeforeFilter);
40,060✔
766
    endRow = TMIN(TMAX(endRow, 0), rowsBeforeFilter);
40,060✔
767
    if (endRow <= startRow) {
40,060✔
768
      continue;
×
769
    }
770

771
    int32_t survivedRows = 0;
40,060✔
772
    for (int32_t r = startRow; r < endRow; ++r) {
80,120✔
773
      if (pIndicator[r]) {
40,060✔
774
        survivedRows++;
20,249✔
775
      }
776
    }
777

778
    if (survivedRows <= 0) {
40,060✔
779
      continue;
19,811✔
780
    }
781

782
    int64_t out = 0;
20,249✔
783
    int32_t* pOutWinIdx = (int32_t*)&out;
20,249✔
784
    int32_t* pOutRowIdx = pOutWinIdx + 1;
20,249✔
785
    *pOutWinIdx = *pCurWinIdx;
20,249✔
786
    *pOutRowIdx = newRowStart;
20,249✔
787
    TSDB_CHECK_NULL(taosArrayPush(pNewIdx, &out), code, lino, _exit, terrno);
20,249✔
788

789
    newRowStart += survivedRows;
20,249✔
790
  }
791

792
  taosArrayClear(pIdx);
8,546✔
793
  int32_t newSize = (int32_t)taosArrayGetSize(pNewIdx);
8,546✔
794
  if (newSize > 0) {
8,546✔
795
    void* dest = taosArrayReserve(pIdx, newSize);
8,094✔
796
    TSDB_CHECK_NULL(dest, code, lino, _exit, terrno);
8,094✔
797
    memcpy(dest, pNewIdx->pData, (size_t)newSize * sizeof(int64_t));
8,094✔
798
  }
799

800
_exit:
452✔
801
  taosArrayDestroy(pNewIdx);
8,546✔
802
  if (code != TSDB_CODE_SUCCESS) {
8,546✔
803
    qError("%s %s failed at line %d since %s", pTaskInfo->id.str, __func__, lino, tstrerror(code));
×
804
  }
805
  return code;
8,546✔
806
}
807

808

809
static int32_t extWinInitCGrpCtx(SExternalWindowOperator* pExtW, SExecTaskInfo* pTaskInfo, SExtWinCalcGrpCtx* pCtx) {
478,525✔
810
  int32_t code = 0, lino = 0;
478,525✔
811

812
  pCtx->groupId = 0;
478,525✔
813
  pCtx->curIdx = 0;
478,525✔
814
  pCtx->lastSKey = INT64_MIN;
478,525✔
815
  pCtx->lastEKey = INT64_MAX;
478,525✔
816
  pCtx->lastWinId = -1;
478,525✔
817
  pCtx->lastWinIdx = -1;
478,525✔
818
  pCtx->blkWinIdx = -1;
478,525✔
819
  pCtx->blkWinStartSet = false;
478,525✔
820
  pCtx->blkWinStartIdx = 0;
478,525✔
821
  pCtx->blkRowStartIdx = 0;
478,525✔
822
  pCtx->outWinIdx = 0;
478,525✔
823
  pCtx->outWinLastIdx = -1;
478,525✔
824
  pCtx->outWinTotalNum = 0;
478,525✔
825
  pCtx->outWinNum = 0;
478,525✔
826
  pCtx->pWins = NULL;
478,525✔
827
  pCtx->outWinBufIdx = NULL;
478,525✔
828
  
829
  SStreamRuntimeFuncInfo* pInfo = &pTaskInfo->pStreamRuntimeInfo->funcInfo;
478,525✔
830

831
  if (pInfo->isMultiGroupCalc) {
478,525✔
832
    pInfo->pStreamPesudoFuncVals = pInfo->curGrpCalc->pParams;
33,375✔
833
    pInfo->pStreamPartColVals = pInfo->curGrpCalc->pGroupColVals;
33,375✔
834
  }
835

836
  size_t size = taosArrayGetSize(pInfo->pStreamPesudoFuncVals);
478,525✔
837
  pCtx->pWins = taosArrayInit_s(sizeof(SExtWinTimeWindow), size);
478,525✔
838
  TSDB_CHECK_NULL(pCtx->pWins, code, lino, _exit, terrno);
478,525✔
839
  //pCtx->outWinBufIdx = taosArrayInit_s(sizeof(int32_t), size);
840
  //TSDB_CHECK_NULL(pCtx->outWinBufIdx, code, lino, _exit, terrno);
841

842
  SExtWinTimeWindow* pWin = taosArrayGet(pCtx->pWins, 0);
478,525✔
843

844
  if (pExtW->isMergeAlignedExtW) {
478,525✔
845
    for (int32_t i = 0; i < size; ++i) {
2,563,645✔
846
      SSTriggerCalcParam* pParam = taosArrayGet(pInfo->pStreamPesudoFuncVals, i);
2,492,445✔
847

848
      pWin[i].tw.skey = pParam->wstart;
2,492,445✔
849
      pWin[i].tw.ekey = pParam->wstart + 1;
2,492,445✔
850
      pWin[i].resWinIdx = -1;
2,492,445✔
851
    }
852
  } else if (pExtW->timeRangeExpr && pExtW->timeRangeExpr->needCalc) {
407,325✔
853
    TAOS_CHECK_EXIT(scalarCalculateExtWinsTimeRange(pExtW->timeRangeExpr, pInfo, pWin));
×
854
    for (int32_t i = 0; i < size; ++i) {
×
855
      pWin[i].resWinIdx = -1;
×
856
    }
857
    if (qDebugFlag & DEBUG_DEBUG) {
×
858
      for (int32_t i = 0; i < size; ++i) {
×
859
        qDebug("%s the %d/%d ext window calced initialized, TR[%" PRId64 ", %" PRId64 ")", 
×
860
            pTaskInfo->id.str, i, (int32_t)size, pWin[i].tw.skey, pWin[i].tw.ekey);
861
      }
862
    }
863
  } else {
864
    for (int32_t i = 0; i < size; ++i) {
23,651,431✔
865
      SSTriggerCalcParam* pParam = taosArrayGet(pInfo->pStreamPesudoFuncVals, i);
23,244,106✔
866

867
      pWin[i].tw.skey = pParam->wstart;
23,243,661✔
868
      pWin[i].tw.ekey = pParam->wend + ((pInfo->triggerType != STREAM_TRIGGER_SLIDING) ? 1 : 0);
23,244,551✔
869
      pWin[i].resWinIdx = -1;
23,244,551✔
870

871
      qDebug("%s the %d/%d ext window initialized, TR[%" PRId64 ", %" PRId64 ")", 
23,244,551✔
872
          pTaskInfo->id.str, i, (int32_t)size, pWin[i].tw.skey, pWin[i].tw.ekey);
873
    }
874
  }
875

876
_exit:
407,325✔
877

878
  if (code) {
478,525✔
879
    qError("%s %s failed at line %d since %s", GET_TASKID(pTaskInfo), __func__, lino, tstrerror(code));
×
880
  }
881
  
882
  return code;
478,525✔
883
}
884

885
static void extWinInitDynParamCGrpCtx(SExtWinCalcGrpCtx* pCtx) {
2,377,675✔
886
  pCtx->groupId = 0;
2,377,675✔
887
  pCtx->curIdx = 0;
2,377,675✔
888
  pCtx->lastSKey = INT64_MIN;
2,377,675✔
889
  pCtx->lastWinId = -1;
2,377,675✔
890
  pCtx->lastWinIdx = -1;
2,377,675✔
891
  pCtx->blkWinIdx = -1;
2,377,675✔
892
  pCtx->blkWinStartSet = false;
2,377,675✔
893
  pCtx->blkWinStartIdx = 0;
2,377,675✔
894
  pCtx->blkRowStartIdx = 0;
2,377,675✔
895
  pCtx->outWinIdx = 0;
2,377,675✔
896
  pCtx->outWinLastIdx = -1;
2,377,675✔
897
  pCtx->outWinTotalNum = 0;
2,377,675✔
898
  pCtx->outWinNum = 0;
2,377,675✔
899
  pCtx->pWins = NULL;
2,377,675✔
900
  pCtx->outWinBufIdx = NULL;
2,377,675✔
901
  pCtx->anyRowCached = false;
2,377,675✔
902
  pCtx->pAnyRow = NULL;
2,377,675✔
903
}
2,377,675✔
904

905
static int32_t extWinSwitchInitTGrpCtx(SExternalWindowOperator* pExtW, SExecTaskInfo* pTaskInfo, SBlockID* pId) {
478,865✔
906
  int32_t code = 0, lino = 0;
478,865✔
907
  SStreamRuntimeFuncInfo* pInfo = &pTaskInfo->pStreamRuntimeInfo->funcInfo;
478,865✔
908

909
  if ((!pInfo->isMultiGroupCalc && NULL != pExtW->pTGrpCtx) ||
478,865✔
910
      (pInfo->isMultiGroupCalc && NULL != pExtW->pTGrpCtx && pId->baseGId == pExtW->lastTGrpId)) {
312,358✔
911
    goto _exit;
168,287✔
912
  }
913
  
914
  if (pInfo->isMultiGroupCalc) {
310,578✔
915
    pInfo->curGrpCalc = tSimpleHashGet(pInfo->pGroupCalcInfos, &pId->baseGId, sizeof(pId->baseGId));
33,375✔
916
    if (NULL == pInfo->curGrpCalc) {
33,375✔
917
      qError("%s %s failed to get %s extWin tgrp %" PRIu64 " calc info", 
×
918
        GET_TASKID(pTaskInfo), __func__, EXT_WIN_TYPE_STR(pExtW->isMergeAlignedExtW), pId->baseGId);
919
      return TSDB_CODE_STREAM_INTERNAL_ERROR;
×
920
    }
921

922
    if (NULL == pInfo->curGrpCalc->pRunnerGrpCtx) {
33,375✔
923
      pInfo->curGrpCalc->pRunnerGrpCtx = taosMemoryCalloc(1, sizeof(SExtWinTrigGrpCtx));
33,375✔
924
      TSDB_CHECK_NULL(pInfo->curGrpCalc->pRunnerGrpCtx, code, lino, _exit, terrno);
33,375✔
925
      
926
      if (!pExtW->calcWithPartition && pExtW->needGroupSort) {
33,375✔
927
        if (NULL == pExtW->pGrpIds) {
×
928
          pExtW->pGrpIds = taosArrayInit(1024, sizeof(uint64_t));
×
929
          TSDB_CHECK_NULL(pExtW->pGrpIds, code, lino, _exit, terrno);
×
930
        }
931
        
932
        TSDB_CHECK_NULL(taosArrayPush(pExtW->pGrpIds, &pId->baseGId), code, lino, _exit, terrno);
×
933
      }
934
    } else {
935
      pInfo->pStreamPesudoFuncVals = pInfo->curGrpCalc->pParams;
×
936
      pInfo->pStreamPartColVals = pInfo->curGrpCalc->pGroupColVals;
×
937
    }
938
    
939
    pInfo->groupId = pId->baseGId;
33,375✔
940
    pExtW->lastTGrpId = pId->baseGId;
33,375✔
941
    pExtW->pTGrpCtx = pInfo->curGrpCalc->pRunnerGrpCtx;
33,375✔
942
    pExtW->ownTGrpCtx = false;
33,375✔
943

944
    qDebug("%s %s extWin switch to tgrp %" PRIu64, 
33,375✔
945
      GET_TASKID(pTaskInfo), EXT_WIN_TYPE_STR(pExtW->isMergeAlignedExtW), pId->baseGId);
946

947
    goto _exit;
33,375✔
948
  }
949

950
  pExtW->pTGrpCtx = taosMemoryCalloc(1, sizeof(SExtWinTrigGrpCtx));
277,203✔
951
  TSDB_CHECK_NULL(pExtW->pTGrpCtx, code, lino, _exit, terrno);
277,203✔
952
  pExtW->ownTGrpCtx = true;
277,203✔
953

954
_exit:
478,865✔
955

956
  if (code) {
478,865✔
957
    qError("%s %s %s extWin failed at line %d since %s", 
×
958
      GET_TASKID(pTaskInfo), __func__, EXT_WIN_TYPE_STR(pExtW->isMergeAlignedExtW), lino, tstrerror(code));
959
  }
960
  
961
  return code;
478,865✔
962
}
963

964
static FORCE_INLINE bool extWinNeedResolvePartitionBlockId(SExternalWindowOperator* pExtW, SExecTaskInfo* pTaskInfo,
965
                                                           const SBlockID* pId) {
966
  if (pId == NULL || pTaskInfo == NULL || pTaskInfo->pStreamRuntimeInfo == NULL) {
562,970✔
967
    return false;
×
968
  }
969

970
  SStreamRuntimeFuncInfo* pInfo = &pTaskInfo->pStreamRuntimeInfo->funcInfo;
562,970✔
971
  return pInfo->isMultiGroupCalc && pExtW->calcWithPartition && pInfo->pGroupCalcInfos != NULL;
562,970✔
972
}
973

974
static void extWinResolveBaseGroupIdForPartition(SExternalWindowOperator* pExtW, SExecTaskInfo* pTaskInfo,
39,605✔
975
                                                 SBlockID* pId) {
976
  SStreamRuntimeFuncInfo* pInfo = &pTaskInfo->pStreamRuntimeInfo->funcInfo;
39,605✔
977

978
  if (pId->baseGId != 0) {
39,605✔
979
    return;
36,490✔
980
  }
981

982
  if (pId->groupId != 0 &&
6,230✔
983
      tSimpleHashGet(pInfo->pGroupCalcInfos, &pId->groupId, sizeof(pId->groupId)) != NULL) {
3,115✔
984
    pId->baseGId = pId->groupId;
3,115✔
985
    qDebug("%s %s normalize baseGId <- groupId %" PRIu64,
3,115✔
986
           GET_TASKID(pTaskInfo), EXT_WIN_TYPE_STR(pExtW->isMergeAlignedExtW), pId->groupId);
987
    return;
3,115✔
988
  }
989

990
  // Compatibility path: some non-stream partitioned external-window plans may
991
  // still send blocks with only `groupId` or with both ids unset on the outer
992
  // side, while runtime has exactly one trigger-group from the subquery. In
993
  // that case we can safely recover `baseGId` from the singleton trigger-group.
994
  //
995
  // Typical SQL shape:
996
  //   select tbname, cast(_wstart as bigint) as ws, cast(ts as bigint) as ts64
997
  //   from ext_cx_src partition by tbname
998
  //   external_window((select ts, endtime, mark from ext_cx_win) w);
999
  //
1000
  // Here the outer query is partitioned (`partition by tbname`), but the
1001
  // subquery has no partition/group clause, so upstream may not fully carry
1002
  // `baseGId` even though there is only one trigger-group to bind against.
1003
  int32_t size = tSimpleHashGetSize(pInfo->pGroupCalcInfos);
×
1004
  if (size == 1) {
×
1005
    int32_t iter = 0;
×
1006
    SSTriggerGroupCalcInfo* pOne = tSimpleHashIterate(pInfo->pGroupCalcInfos, NULL, &iter);
×
1007
    if (pOne != NULL) {
×
1008
      pId->baseGId = *(uint64_t*)tSimpleHashGetKey(pOne, NULL);
×
1009
      qDebug("%s %s normalize baseGId <- single gid %" PRIu64,
×
1010
             GET_TASKID(pTaskInfo), EXT_WIN_TYPE_STR(pExtW->isMergeAlignedExtW), pId->baseGId);
1011
    }
1012
  }
1013
}
1014

1015
static void extWinResolveCalcGroupIdForPartition(SExternalWindowOperator* pExtW, SExecTaskInfo* pTaskInfo,
39,605✔
1016
                                                 SBlockID* pId) {
1017
  if (pId->groupId == 0 && pId->baseGId != 0) {
39,605✔
1018
    pId->groupId = pId->baseGId;
×
1019
    qDebug("%s %s normalize groupId <- baseGId %" PRIu64,
×
1020
           GET_TASKID(pTaskInfo), EXT_WIN_TYPE_STR(pExtW->isMergeAlignedExtW), pId->groupId);
1021
  }
1022
}
39,605✔
1023

1024
static void extWinResolveBlockIdForPartition(SExternalWindowOperator* pExtW, SExecTaskInfo* pTaskInfo, SBlockID* pId) {
562,970✔
1025
  if (!extWinNeedResolvePartitionBlockId(pExtW, pTaskInfo, pId)) {
562,970✔
1026
    return;
523,365✔
1027
  }
1028

1029
  extWinResolveBaseGroupIdForPartition(pExtW, pTaskInfo, pId);
39,605✔
1030
  extWinResolveCalcGroupIdForPartition(pExtW, pTaskInfo, pId);
39,605✔
1031
}
1032

1033

1034
static int32_t extWinSwitchInitCGrpCtx(SExternalWindowOperator* pExtW, SExecTaskInfo* pTaskInfo, SBlockID* pId) {
478,865✔
1035
  int32_t code = 0, lino = 0;
478,865✔
1036
  SStreamRuntimeFuncInfo* pInfo = &pTaskInfo->pStreamRuntimeInfo->funcInfo;
478,865✔
1037
  SExtWinTrigGrpCtx* pTCtx = pExtW->pTGrpCtx;
478,865✔
1038

1039
  if (pTCtx == NULL) {
478,865✔
1040
    qError("%s %s invalid tgrp ctx for %s extWin, baseGrp:%" PRIu64 " grp:%" PRIu64,
×
1041
           GET_TASKID(pTaskInfo), __func__, EXT_WIN_TYPE_STR(pExtW->isMergeAlignedExtW),
1042
           pId->baseGId, pId->groupId);
1043
    TAOS_CHECK_EXIT(TSDB_CODE_STREAM_INTERNAL_ERROR);
×
1044
  }
1045

1046
  // In partitioned multi-group mode, only blocks whose C-group (groupId)
1047
  // matches the current T-group (baseGId) should proceed. Treat mismatches
1048
  // like "no cgrp" here; extWinOpen has an early filter to skip such blocks.
1049
  if (0 == pId->groupId || (pInfo->isMultiGroupCalc && (pId->baseGId != pId->groupId))) {
478,865✔
1050
    if (NULL != pTCtx->pCGCtxs) {
206,343✔
1051
      qError("%s plan or ctx conflict, pCGCtxs:%p baseGrp:%" PRIu64 " grp:%" PRIu64
×
1052
             " lastCGrp:%" PRIu64 " isMulti:%d calcWithPart:%d",
1053
             GET_TASKID(pTaskInfo), pTCtx->pCGCtxs, pId->baseGId, pId->groupId,
1054
             pExtW->lastCGrpId, pInfo->isMultiGroupCalc, pExtW->calcWithPartition);
1055
      TAOS_CHECK_EXIT(TSDB_CODE_STREAM_INTERNAL_ERROR);
×
1056
    }
1057

1058
    if (NULL == pTCtx->pCCtx) {
206,343✔
1059
      pTCtx->pCCtx = taosMemoryCalloc(1, sizeof(*pTCtx->pCCtx));
194,773✔
1060
      TSDB_CHECK_NULL(pTCtx->pCCtx, code, lino, _exit, terrno);
194,773✔
1061
      TAOS_CHECK_EXIT(extWinInitCGrpCtx(pExtW, pTaskInfo, pTCtx->pCCtx));
194,773✔
1062
    }
1063

1064
    qDebug("%s ext win switch to no cgrp", EXT_WIN_TYPE_STR(pExtW->isMergeAlignedExtW));
206,343✔
1065
    
1066
    goto _exit;
206,343✔
1067
  }
1068

1069
  if (pId->groupId == pExtW->lastCGrpId) {
272,522✔
1070
    if (pTCtx->pCCtx != NULL) {
4,450✔
1071
      pTCtx->pCCtx->groupId = pId->groupId;
4,450✔
1072
    }
1073
    qDebug("%s ext win continue cgrp %" PRIu64, EXT_WIN_TYPE_STR(pExtW->isMergeAlignedExtW), pId->groupId);
4,450✔
1074
    goto _exit;
4,450✔
1075
  }
1076

1077
  pExtW->lastCGrpId = pId->groupId;
268,072✔
1078
  
1079
  if (NULL == pTCtx->pCGCtxs) {
268,072✔
1080
    if (NULL != pTCtx->pCCtx) {
115,805✔
1081
      TAOS_CHECK_EXIT(TSDB_CODE_STREAM_INTERNAL_ERROR);
×
1082
    }
1083
    
1084
    pTCtx->pCGCtxs = tSimpleHashInit(EXT_WIN_CALC_GROUP_SIZE, taosGetDefaultHashFunction(TSDB_DATA_TYPE_UBIGINT));
115,805✔
1085
    TSDB_CHECK_NULL(pTCtx->pCGCtxs, code, lino, _exit, terrno);
115,805✔
1086
  }
1087

1088
  pTCtx->pCCtx = tSimpleHashGet(pTCtx->pCGCtxs, &pId->groupId, sizeof(pId->groupId));
268,072✔
1089
  if (NULL == pTCtx->pCCtx) {
268,072✔
1090
    SExtWinCalcGrpCtx tmp = {0};
268,072✔
1091
    TAOS_CHECK_EXIT(tSimpleHashPut(pTCtx->pCGCtxs, &pId->groupId, sizeof(pId->groupId), &tmp, sizeof(tmp)));
268,072✔
1092
    pTCtx->pCCtx = tSimpleHashGet(pTCtx->pCGCtxs, &pId->groupId, sizeof(pId->groupId));
268,072✔
1093
    TAOS_CHECK_EXIT(extWinInitCGrpCtx(pExtW, pTaskInfo, pTCtx->pCCtx));
268,072✔
1094

1095
    if (pExtW->needGroupSort) {
268,072✔
1096
      if (pInfo->isMultiGroupCalc) {
36,490✔
1097
        if (NULL == pExtW->pCTGrpIds) {
6,230✔
1098
          pExtW->pCTGrpIds = taosArrayInit(1024, sizeof(uint64_t) * 2);
4,895✔
1099
          TSDB_CHECK_NULL(pExtW->pCTGrpIds, code, lino, _exit, terrno);
4,895✔
1100
        }
1101
        
1102
        TSDB_CHECK_NULL(taosArrayPush(pExtW->pCTGrpIds, &pId->groupId), code, lino, _exit, terrno);
12,460✔
1103
      } else {
1104
        if (NULL == pExtW->pGrpIds) {
30,260✔
1105
          pExtW->pGrpIds = taosArrayInit(1024, sizeof(uint64_t));
22,250✔
1106
          TSDB_CHECK_NULL(pExtW->pGrpIds, code, lino, _exit, terrno);
22,250✔
1107
        }
1108
        
1109
        TSDB_CHECK_NULL(taosArrayPush(pExtW->pGrpIds, &pId->groupId), code, lino, _exit, terrno);
60,520✔
1110
      }
1111
    }
1112
  }
1113
  if (pTCtx->pCCtx != NULL) {
268,072✔
1114
    pTCtx->pCCtx->groupId = pId->groupId;
268,072✔
1115
  }
1116
  
1117
  qDebug("%s ext win switch to cgrp %" PRIu64, EXT_WIN_TYPE_STR(pExtW->isMergeAlignedExtW), pId->groupId);
268,072✔
1118

1119
_exit:
×
1120

1121
  if (code) {
478,865✔
1122
    qError("%s %s %s ext win failed at line %d since %s", 
×
1123
      GET_TASKID(pTaskInfo), __func__, EXT_WIN_TYPE_STR(pExtW->isMergeAlignedExtW), lino, tstrerror(code));
1124
  }
1125
  
1126
  return code;
478,865✔
1127
}
1128

1129
static int32_t extWinSwitchInitCtxs(SExternalWindowOperator* pExtW, SExecTaskInfo* pTaskInfo, SBlockID* pId) {
4,171,445✔
1130
  int32_t code = 0, lino = 0;
4,171,445✔
1131

1132
  if (pTaskInfo->pStreamRuntimeInfo == NULL) {
4,171,445✔
1133
    return TSDB_CODE_SUCCESS;
3,692,580✔
1134
  }
1135

1136
  extWinResolveBlockIdForPartition(pExtW, pTaskInfo, pId);
478,865✔
1137
  TAOS_CHECK_EXIT(extWinSwitchInitTGrpCtx(pExtW, pTaskInfo, pId));
478,865✔
1138
  TAOS_CHECK_EXIT(extWinSwitchInitCGrpCtx(pExtW, pTaskInfo, pId));
478,865✔
1139

1140
_exit:
478,865✔
1141

1142
  if (code) {
478,865✔
1143
    qError("%s %s %s ext win failed at line %d since %s", 
×
1144
      GET_TASKID(pTaskInfo), __func__, EXT_WIN_TYPE_STR(pExtW->isMergeAlignedExtW), lino, tstrerror(code));
1145
  }
1146

1147
  return code;
478,865✔
1148
}
1149

1150

1151
static void extWinResetResultRows(SExtWinResultRows* pRows) {
×
1152
  pRows->resRowsIdx = 0;
×
1153
  pRows->resRowIdx = 0;
×
1154
}
×
1155

1156
static int32_t mergeAlignExtWinSetOutputBuf(SOperatorInfo* pOperator, SResultRowInfo* pResultRowInfo, const STimeWindow* pWin, SResultRow** pResult,
2,473,755✔
1157
                                       SExprSupp* pExprSup, SAggSupporter* pAggSup) {
1158
  if (*pResult == NULL) {
2,473,755✔
1159
    *pResult = getNewResultRow(pAggSup->pResultBuf, &pAggSup->currentPageId, pAggSup->resultRowSize);
62,300✔
1160
    if (!*pResult) {
62,300✔
1161
      qError("get new resultRow failed, err:%s", tstrerror(terrno));
×
1162
      return terrno;
×
1163
    }
1164
    pResultRowInfo->cur = (SResultRowPosition){.pageId = (*pResult)->pageId, .offset = (*pResult)->offset};
62,300✔
1165
  }
1166
  
1167
  (*pResult)->win = *pWin;
2,473,755✔
1168
  (*pResult)->winIdx = extWinGetCurWinIdx(pOperator);
2,473,755✔
1169
  
1170
  return setResultRowInitCtx((*pResult), pExprSup->pCtx, pExprSup->numOfExprs, pExprSup->rowEntryInfoOffset);
2,473,755✔
1171
}
1172

1173

1174
static int32_t mergeAlignExtWinGetWinFromTs(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW, TSKEY ts, STimeWindow** ppWin) {
2,473,755✔
1175
  int32_t blkWinIdx = extWinGetCurWinIdx(pOperator);
2,473,755✔
1176
  
1177
  // TODO handle desc order
1178
  for (int32_t i = blkWinIdx; i < pExtW->pTGrpCtx->pCCtx->pWins->size; ++i) {
15,132,225✔
1179
    STimeWindow* pWin = taosArrayGet(pExtW->pTGrpCtx->pCCtx->pWins, i);
15,132,225✔
1180
    if (ts == pWin->skey) {
15,132,225✔
1181
      extWinSetCurWinIdx(pOperator, i);
2,473,755✔
1182
      *ppWin = pWin;
2,473,755✔
1183
      return TSDB_CODE_SUCCESS;
2,473,755✔
1184
    } else if (ts < pWin->skey) {
12,658,470✔
1185
      qError("invalid ts %" PRId64 " for tgrp %" PRIu64 " current window idx %d skey %" PRId64, ts, pExtW->lastTGrpId, i, pWin->skey);
×
1186
      return TSDB_CODE_STREAM_INTERNAL_ERROR;
×
1187
    }
1188
  }
1189
  
1190
  qError("invalid ts %" PRId64 " to find tgrp %" PRIu64" merge aligned ext window, size:%d", ts, pExtW->lastTGrpId, (int32_t)pExtW->pTGrpCtx->pCCtx->pWins->size);
×
1191
  
1192
  return TSDB_CODE_STREAM_INTERNAL_ERROR;
×
1193
}
1194

1195
static int32_t mergeAlignExtWinFinalizeResult(SOperatorInfo* pOperator, SResultRowInfo* pResultRowInfo, SSDataBlock* pResultBlock) {
2,473,755✔
1196
  int32_t        code = 0, lino = 0;
2,473,755✔
1197
  SMergeAlignedExternalWindowOperator* pMAExtW = pOperator->info;
2,473,755✔
1198
  SExternalWindowOperator*             pExtW = pMAExtW->pExtW;
2,473,755✔
1199
  SExprSupp*     pSup = &pOperator->exprSupp;
2,473,755✔
1200
  SResultRow*  pResultRow = pMAExtW->pResultRow;
2,473,755✔
1201
  
1202
  finalizeResultRows(pExtW->aggSup.pResultBuf, &pResultRowInfo->cur, pSup, pResultBlock, pOperator->pTaskInfo);
2,473,755✔
1203

1204
  if (pResultRow->numOfRows > 0) {
2,473,755✔
1205
    TAOS_CHECK_EXIT(extWinAppendWinIdx(pOperator->pTaskInfo, pExtW->pWinRowIdx, pResultBlock, pResultRow->winIdx, pResultRow->numOfRows));
2,473,755✔
1206
  }
1207

1208
_exit:
2,473,755✔
1209

1210
  if (code) {
2,473,755✔
1211
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
1212
  }
1213

1214
  return code;
2,473,755✔
1215
}
1216

1217
// For vtable COLS merge (isDynWindow): fill skipped windows [fromIdx, toIdx) with NULL rows.
1218
// Each COLS merge source must output exactly numOfWins rows so columns align positionally.
1219
static int32_t mergeAlignExtWinFillEmptyWins(SOperatorInfo* pOperator, SResultRowInfo* pResultRowInfo,
×
1220
                                              SSDataBlock* pResultBlock, int32_t fromIdx, int32_t toIdx) {
1221
  SMergeAlignedExternalWindowOperator* pMAExtW = pOperator->info;
×
1222
  SExternalWindowOperator*             pExtW = pMAExtW->pExtW;
×
1223
  SExprSupp*                           pSup = &pOperator->exprSupp;
×
1224
  int32_t                              code = 0, lino = 0;
×
1225

1226
  for (int32_t i = fromIdx; i < toIdx; ++i) {
×
1227
    STimeWindow* pWin = taosArrayGet(pExtW->pTGrpCtx->pCCtx->pWins, i);
×
1228
    if (pWin == NULL) continue;
×
1229

1230
    extWinSetCurWinIdx(pOperator, i);
×
1231
    TAOS_CHECK_EXIT(mergeAlignExtWinSetOutputBuf(pOperator, pResultRowInfo, pWin, &pMAExtW->pResultRow, pSup, &pExtW->aggSup));
×
1232
    TAOS_CHECK_EXIT(mergeAlignExtWinFinalizeResult(pOperator, pResultRowInfo, pResultBlock));
×
1233
    resetResultRow(pMAExtW->pResultRow, pExtW->aggSup.resultRowSize - sizeof(SResultRow));
×
1234
    pMAExtW->lastFinalizedWinIdx = i;
×
1235
  }
1236

1237
_exit:
×
1238
  if (code) {
×
1239
    qError("%s %s failed at line %d since %s", GET_TASKID(pOperator->pTaskInfo), __func__, lino, tstrerror(code));
×
1240
  }
1241
  return code;
×
1242
}
1243

1244
static int32_t mergeAlignExtWinAggDo(SOperatorInfo* pOperator, SResultRowInfo* pResultRowInfo, SSDataBlock* pBlock, SSDataBlock* pResultBlock) {
75,205✔
1245
  SMergeAlignedExternalWindowOperator* pMAExtW = pOperator->info;
75,205✔
1246
  SExternalWindowOperator*             pExtW = pMAExtW->pExtW;
75,205✔
1247

1248
  SExecTaskInfo* pTaskInfo = pOperator->pTaskInfo;
75,205✔
1249
  SExprSupp*     pSup = &pOperator->exprSupp;
75,205✔
1250
  int32_t        code = 0, lino = 0;
75,205✔
1251
  STimeWindow *pWin = NULL;
75,205✔
1252

1253
  int32_t startPos = 0;
75,205✔
1254
  int64_t* tsCols = extWinExtractTsCol(pBlock, pExtW->primaryTsIndex, pTaskInfo);
75,205✔
1255
  TSKEY ts = getStartTsKey(&pBlock->info.window, tsCols);
75,205✔
1256

1257
  extWinSetCurWinIdx(pOperator, 0);
75,205✔
1258
  code = mergeAlignExtWinGetWinFromTs(pOperator, pExtW, ts, &pWin);
75,205✔
1259
  if (code) {
75,205✔
1260
    qError("failed to get time window for tgrp %" PRIu64 " ts:%" PRId64 ", prim ts index:%d, error:%s", 
×
1261
      pExtW->lastTGrpId, ts, pExtW->primaryTsIndex, tstrerror(code));
1262
    TAOS_CHECK_EXIT(code);
×
1263
  }
1264

1265
  int32_t newWinIdx = extWinGetCurWinIdx(pOperator);
75,205✔
1266

1267
  if (pMAExtW->curTs != INT64_MIN && pMAExtW->curTs != pWin->skey) {
75,205✔
1268
    TAOS_CHECK_EXIT(mergeAlignExtWinFinalizeResult(pOperator, pResultRowInfo, pResultBlock));
4,005✔
1269
    pMAExtW->lastFinalizedWinIdx = pMAExtW->pResultRow->winIdx;
4,005✔
1270
    resetResultRow(pMAExtW->pResultRow, pExtW->aggSup.resultRowSize - sizeof(SResultRow));
4,005✔
1271
  }
1272

1273
  if (pExtW->isDynWindow && pMAExtW->lastFinalizedWinIdx + 1 < newWinIdx) {
75,205✔
1274
    TAOS_CHECK_EXIT(mergeAlignExtWinFillEmptyWins(pOperator, pResultRowInfo, pResultBlock, pMAExtW->lastFinalizedWinIdx + 1, newWinIdx));
×
1275
    extWinSetCurWinIdx(pOperator, newWinIdx);
×
1276
  }
1277

1278
  TAOS_CHECK_EXIT(mergeAlignExtWinSetOutputBuf(pOperator, pResultRowInfo, pWin, &pMAExtW->pResultRow, pSup, &pExtW->aggSup));
75,205✔
1279

1280
  int32_t currPos = startPos;
75,205✔
1281
  pMAExtW->curTs = pWin->skey;
75,205✔
1282
  
1283
  while (++currPos < pBlock->info.rows) {
4,807,335✔
1284
    if (tsCols[currPos] == pMAExtW->curTs) continue;
4,732,130✔
1285

1286
    qDebug("current ts:%" PRId64 ", startPos:%d, currPos:%d, tsCols[currPos]:%" PRId64,
2,398,550✔
1287
      pMAExtW->curTs, startPos, currPos, tsCols[currPos]); 
1288
    TAOS_CHECK_EXIT(applyAggFunctionOnPartialTuples(pTaskInfo, pSup->pCtx, &pExtW->twAggSup.timeWindowData, startPos,
2,398,550✔
1289
                                           currPos - startPos, pBlock->info.rows, pSup->numOfExprs));
1290

1291
    TAOS_CHECK_EXIT(mergeAlignExtWinFinalizeResult(pOperator, pResultRowInfo, pResultBlock));
2,398,550✔
1292
    pMAExtW->lastFinalizedWinIdx = pMAExtW->pResultRow->winIdx;
2,398,550✔
1293
    resetResultRow(pMAExtW->pResultRow, pExtW->aggSup.resultRowSize - sizeof(SResultRow));
2,398,550✔
1294

1295
    TAOS_CHECK_EXIT(mergeAlignExtWinGetWinFromTs(pOperator, pExtW, tsCols[currPos], &pWin));
2,398,550✔
1296
    newWinIdx = extWinGetCurWinIdx(pOperator);
2,398,550✔
1297

1298
    if (pExtW->isDynWindow && pMAExtW->lastFinalizedWinIdx + 1 < newWinIdx) {
2,398,550✔
1299
      TAOS_CHECK_EXIT(mergeAlignExtWinFillEmptyWins(pOperator, pResultRowInfo, pResultBlock, pMAExtW->lastFinalizedWinIdx + 1, newWinIdx));
×
1300
      extWinSetCurWinIdx(pOperator, newWinIdx);
×
1301
    }
1302

1303
    qDebug("ext window align2 start:%" PRId64 ", end:%" PRId64, pWin->skey, pWin->ekey);
2,398,550✔
1304
    startPos = currPos;
2,398,550✔
1305
    
1306
    TAOS_CHECK_EXIT(mergeAlignExtWinSetOutputBuf(pOperator, pResultRowInfo, pWin, &pMAExtW->pResultRow, pSup, &pExtW->aggSup));
2,398,550✔
1307

1308
    pMAExtW->curTs = pWin->skey;
2,398,550✔
1309
  }
1310

1311
  code = applyAggFunctionOnPartialTuples(pTaskInfo, pSup->pCtx, &pExtW->twAggSup.timeWindowData, startPos,
150,410✔
1312
                                         currPos - startPos, pBlock->info.rows, pSup->numOfExprs);
75,205✔
1313

1314
_exit:
75,205✔
1315

1316
  if (code != 0) {
75,205✔
1317
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
1318
    T_LONG_JMP(pTaskInfo->env, code);
×
1319
  }
1320
  
1321
  return code;
75,205✔
1322
}
1323

1324
static int32_t mergeAlignExtWinBuildWinRowIdx(SOperatorInfo* pOperator, SSDataBlock* pInput, SSDataBlock* pResult) {
×
1325
  SExternalWindowOperator* pExtW = pOperator->info;
×
1326
  int64_t* tsCols = extWinExtractTsCol(pInput, pExtW->primaryTsIndex, pOperator->pTaskInfo);
×
1327
  STimeWindow* pWin = NULL;
×
1328
  int32_t code = 0, lino = 0;
×
1329
  int64_t prevTs = INT64_MIN;
×
1330
  
1331
  for (int32_t i = 0; i < pInput->info.rows; ++i) {
×
1332
    if (prevTs == tsCols[i]) {
×
1333
      continue;
×
1334
    }
1335
    
1336
    TAOS_CHECK_EXIT(mergeAlignExtWinGetWinFromTs(pOperator, pExtW, tsCols[i], &pWin));
×
1337
    TAOS_CHECK_EXIT(extWinAppendWinIdx(pOperator->pTaskInfo, pExtW->pWinRowIdx, pResult, extWinGetCurWinIdx(pOperator), pInput->info.rows - i));
×
1338

1339
    prevTs = tsCols[i];
×
1340
  }
1341

1342
_exit:
×
1343

1344
  if (code != 0) {
×
1345
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
1346
  }
1347

1348
  return code;  
×
1349
}
1350

1351
static int32_t mergeAlignExtWinProjectDo(SOperatorInfo* pOperator, SResultRowInfo* pResultRowInfo, SSDataBlock* pBlock,
×
1352
                                            SSDataBlock* pResultBlock) {
1353
  SExternalWindowOperator* pExtW = pOperator->info;
×
1354
  SExprSupp*               pExprSup = &pExtW->scalarSupp;
×
1355
  int32_t                  code = 0, lino = 0;
×
1356
  
1357
  TAOS_CHECK_EXIT(projectApplyFunctions(pExprSup->pExprInfo, pResultBlock, pBlock, pExprSup->pCtx, pExprSup->numOfExprs, NULL,
×
1358
                        GET_STM_RTINFO(pOperator->pTaskInfo), pOperator->pTaskInfo));
1359

1360
  TAOS_CHECK_EXIT(mergeAlignExtWinBuildWinRowIdx(pOperator, pBlock, pResultBlock));
×
1361

1362
_exit:
×
1363

1364
  if (code != 0) {
×
1365
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
1366
  }
1367

1368
  return code;
×
1369
}
1370

1371
static void mergeAlignExtWinDo(SOperatorInfo* pOperator) {
72,535✔
1372
  SExecTaskInfo*                       pTaskInfo = pOperator->pTaskInfo;
72,535✔
1373
  SMergeAlignedExternalWindowOperator* pMAExtW = pOperator->info;
72,535✔
1374
  SExternalWindowOperator*             pExtW = pMAExtW->pExtW;
72,535✔
1375
  SResultRow*                          pResultRow = NULL;
72,535✔
1376
  int32_t                              code = 0;
72,535✔
1377
  SSDataBlock*                         pRes = pExtW->binfo.pRes;
72,535✔
1378
  SExprSupp*                           pSup = &pOperator->exprSupp;
72,535✔
1379
  int32_t                              lino = 0;
72,535✔
1380
  SStreamRuntimeFuncInfo*              pStream = &pOperator->pTaskInfo->pStreamRuntimeInfo->funcInfo;
72,535✔
1381

1382
  taosArrayClear(pExtW->pWinRowIdx);
72,535✔
1383
  blockDataCleanup(pRes);
72,535✔
1384

1385
  SSDataBlock* pBlock = NULL;
72,535✔
1386
  while (1) {
1387
    if (pMAExtW->pNewGroup != NULL) {
147,295✔
1388
      pBlock = pMAExtW->pNewGroup;
8,900✔
1389
      pMAExtW->pNewGroup = NULL;
8,900✔
1390
    } else {
1391
      pBlock = getNextBlockFromDownstream(pOperator, 0);
138,395✔
1392
    }
1393

1394
    if (pBlock == NULL) {
147,295✔
1395
      // close last time window
1396
      if (pMAExtW->curTs != INT64_MIN && EEXT_MODE_AGG == pExtW->mode) {
63,190✔
1397
        TAOS_CHECK_EXIT(mergeAlignExtWinFinalizeResult(pOperator, &pExtW->binfo.resultRowInfo, pRes));
62,300✔
1398
        pMAExtW->lastFinalizedWinIdx = pMAExtW->pResultRow->winIdx;
62,300✔
1399
      }
1400
      // fill remaining empty windows with NULL rows (only for virtual table COLS merge)
1401
      if (pExtW->isDynWindow && EEXT_MODE_AGG == pExtW->mode && pExtW->pTGrpCtx && pExtW->pTGrpCtx->pCCtx && pExtW->pTGrpCtx->pCCtx->pWins) {
63,190✔
1402
        int32_t totalWins = taosArrayGetSize(pExtW->pTGrpCtx->pCCtx->pWins);
×
1403
        if (pMAExtW->lastFinalizedWinIdx + 1 < totalWins) {
×
1404
          TAOS_CHECK_EXIT(mergeAlignExtWinFillEmptyWins(pOperator, &pExtW->binfo.resultRowInfo, pRes, pMAExtW->lastFinalizedWinIdx + 1, totalWins));
×
1405
        }
1406
      }
1407
      setOperatorCompleted(pOperator);
63,190✔
1408
      break;
63,190✔
1409
    }
1410

1411
    extWinResolveBlockIdForPartition(pExtW, pTaskInfo, &pBlock->info.id);
84,105✔
1412

1413
    if (pExtW->lastCGrpId != pBlock->info.id.groupId) {
84,105✔
1414
      if (pMAExtW->curTs != INT64_MIN && EEXT_MODE_AGG == pExtW->mode) {
27,145✔
1415
        TAOS_CHECK_EXIT(mergeAlignExtWinFinalizeResult(pOperator, &pExtW->binfo.resultRowInfo, pRes));
8,900✔
1416

1417
        // Group boundary: always clear row state and curTs after finalize to avoid
1418
        // duplicate finalization and cross-group state reuse on next block.
1419

1420
        if (pMAExtW->pResultRow != NULL) {
8,900✔
1421
          resetResultRow(pMAExtW->pResultRow, pExtW->aggSup.resultRowSize - sizeof(SResultRow));
8,900✔
1422
        }
1423

1424
        pMAExtW->curTs = INT64_MIN;
8,900✔
1425
      }
1426

1427
      if (pRes->info.rows > 0) {
27,145✔
1428
        pMAExtW->pNewGroup = pBlock;
8,900✔
1429
        pMAExtW->curTs = INT64_MIN;
8,900✔
1430
        break;
8,900✔
1431
      }
1432
    }
1433

1434
    TAOS_CHECK_EXIT(extWinSwitchInitCtxs(pExtW, pTaskInfo, &pBlock->info.id));
75,205✔
1435

1436
    pRes->info.scanFlag = pBlock->info.scanFlag;
75,205✔
1437
    pRes->info.id.groupId = pBlock->info.id.baseGId;  // only keep TGroup ID
75,205✔
1438

1439
    code = setInputDataBlock(pSup, pBlock, pExtW->binfo.inputTsOrder, pBlock->info.scanFlag, true);
75,205✔
1440
    QUERY_CHECK_CODE(code, lino, _exit);
75,205✔
1441

1442
    printDataBlock(pBlock, __func__, "externalwindowAlign", pTaskInfo->id.queryId);
75,205✔
1443

1444
    if (EEXT_MODE_SCALAR == pExtW->mode) {
75,205✔
1445
      TAOS_CHECK_EXIT(mergeAlignExtWinProjectDo(pOperator, &pExtW->binfo.resultRowInfo, pBlock, pRes));
×
1446
    } else {
1447
      TAOS_CHECK_EXIT(mergeAlignExtWinAggDo(pOperator, &pExtW->binfo.resultRowInfo, pBlock, pRes));
75,205✔
1448
    }
1449

1450
    if (pRes->info.rows >= pOperator->resultInfo.threshold) {
75,205✔
1451
      break;
445✔
1452
    }
1453
  }
1454

1455
  pStream->pStreamBlkWinIdx = pExtW->pWinRowIdx;
72,535✔
1456

1457
  extWinPostUpdateStreamRt(pStream, pOperator, pExtW);
72,535✔
1458
  
1459
_exit:
72,535✔
1460

1461
  if (code != 0) {
72,535✔
1462
    qError("%s failed at line %d since:%s", __func__, lino, tstrerror(code));
×
1463
    pTaskInfo->code = code;
×
1464
    T_LONG_JMP(pTaskInfo->env, code);
×
1465
  }
1466
}
72,535✔
1467

1468
static int32_t mergeAlignExtWinNext(SOperatorInfo* pOperator, SSDataBlock** ppRes) {
133,500✔
1469
  SExecTaskInfo*                       pTaskInfo = pOperator->pTaskInfo;
133,500✔
1470
  SMergeAlignedExternalWindowOperator* pMAExtW = pOperator->info;
133,500✔
1471
  SExternalWindowOperator*             pExtW = pMAExtW->pExtW;
133,500✔
1472
  int32_t                              code = 0;
133,500✔
1473
  int32_t lino = 0;
133,500✔
1474

1475
  if (pOperator->status == OP_EXEC_DONE) {
133,500✔
1476
    (*ppRes) = NULL;
60,965✔
1477
    return TSDB_CODE_SUCCESS;
60,965✔
1478
  }
1479

1480
  SSDataBlock* pRes = pExtW->binfo.pRes;
72,535✔
1481
  blockDataCleanup(pRes);
72,535✔
1482

1483
  mergeAlignExtWinDo(pOperator);
72,535✔
1484

1485
  if (pRes->info.rows > 0 && pOperator->exprSupp.pFilterInfo != NULL) {
72,535✔
1486
    SColumnInfoData* pFilterRes = NULL;
7,120✔
1487
    SColMatchInfo* pMatchInfo = pExtW->fillMatchInfo.pList != NULL ? &pExtW->fillMatchInfo : NULL;
7,120✔
1488
    TAOS_CHECK_EXIT(doFilter(pRes, pOperator->exprSupp.pFilterInfo, pMatchInfo, &pFilterRes));
7,120✔
1489
    colDataDestroy(pFilterRes);
7,120✔
1490
    taosMemoryFree(pFilterRes);
7,120✔
1491
  }
1492

1493
  size_t rows = pRes->info.rows;
72,535✔
1494
  (*ppRes) = (rows == 0) ? NULL : pRes;
72,535✔
1495

1496
_exit:
72,535✔
1497

1498
  if (code != 0) {
72,535✔
1499
    qError("%s failed at line %d since:%s", __func__, lino, tstrerror(code));
×
1500
    pTaskInfo->code = code;
×
1501
    T_LONG_JMP(pTaskInfo->env, code);
×
1502
  }
1503
  return code;
72,535✔
1504
}
1505

1506
static int32_t resetMergeAlignedExtWinOperator(SOperatorInfo* pOperator) {
×
1507
  int32_t code = 0, lino = 0;
×
1508
  SMergeAlignedExternalWindowOperator* pMAExtW = pOperator->info;
×
1509
  SExternalWindowOperator*             pExtW = pMAExtW->pExtW;
×
1510
  SExecTaskInfo*                       pTaskInfo = pOperator->pTaskInfo;
×
1511
  SMergeAlignedIntervalPhysiNode * pPhynode = (SMergeAlignedIntervalPhysiNode*)pOperator->pPhyNode;
×
1512
  pOperator->status = OP_NOT_OPENED;
×
1513

1514
  //resetBasicOperatorState(&pExtW->binfo);
1515
  //pMAExtW->pResultRow = NULL;
1516

1517
  initResultRowInfo(&pExtW->binfo.resultRowInfo);
×
1518
  pMAExtW->curTs = INT64_MIN;
×
1519
  pMAExtW->lastFinalizedWinIdx = -1;
×
1520

1521
  extWinResetResultRows(&pExtW->resultRows);
×
1522

1523
/*
1524
  int32_t code = resetAggSup(&pOperator->exprSupp, &pExtW->aggSup, pTaskInfo, pPhynode->window.pFuncs, NULL,
1525
                             sizeof(int64_t) * 2 + POINTER_BYTES, pTaskInfo->id.str, NULL,
1526
                             &pTaskInfo->storageAPI.functionStore);
1527
*/                             
1528

1529
  colDataDestroy(&pExtW->twAggSup.timeWindowData);
×
1530
  TAOS_CHECK_EXIT(initExecTimeWindowInfo(&pExtW->twAggSup.timeWindowData, &pTaskInfo->window));
×
1531

1532
  pExtW->outWinIdx = 0;
×
1533
  pExtW->lastTGrpId = 0;
×
1534
  pExtW->lastCGrpId = 0;
×
1535
  // ownTGrpCtx==true: this operator owns the allocation; free before reset.
1536
  // ownTGrpCtx==false: borrowed pointer into pGroupCalcInfos; must not free.
1537
  if (pExtW->ownTGrpCtx && pExtW->pTGrpCtx) {
×
1538
    extWinDestroyTGrpCtx(pExtW->pTGrpCtx);
×
1539
    taosMemoryFree(pExtW->pTGrpCtx);
×
1540
  }
1541
  pExtW->pTGrpCtx = NULL;
×
1542
  pExtW->ownTGrpCtx = false;
×
1543

1544
_exit:
×
1545

1546
  if (code != 0) {
×
1547
    qError("%s failed at line %d since:%s", __func__, lino, tstrerror(code));
×
1548
  }
1549
  
1550
  return code;
×
1551
}
1552

1553
static int32_t extWinBuildFillMatchInfo(SColMatchInfo* pMatchInfo, SNodeList* pFillExprs) {
1,670,890✔
1554
  if (pFillExprs == NULL) {
1,670,890✔
1555
    return TSDB_CODE_SUCCESS;
1,650,168✔
1556
  }
1557

1558
  int32_t numFillExprs = LIST_LENGTH(pFillExprs);
20,722✔
1559
  pMatchInfo->matchType = COL_MATCH_FROM_SLOT_ID;
20,722✔
1560
  pMatchInfo->pList = taosArrayInit(numFillExprs, sizeof(SColMatchItem));
20,722✔
1561
  if (pMatchInfo->pList == NULL) {
20,722✔
1562
    return terrno;
×
1563
  }
1564

1565
  SNode* pFNode = NULL;
20,722✔
1566
  FOREACH(pFNode, pFillExprs) {
51,770✔
1567
    int16_t   dstSlot = -1;
31,048✔
1568
    SDataType dtype = {0};
31,048✔
1569
    int16_t   colId = 0;
31,048✔
1570
    bool      isPk = false;
31,048✔
1571
    bool      found = false;
31,048✔
1572

1573
    if (nodeType(pFNode) == QUERY_NODE_TARGET) {
31,048✔
1574
      STargetNode* pTarget = (STargetNode*)pFNode;
×
1575
      dstSlot = pTarget->slotId;
×
1576
      if (nodeType(pTarget->pExpr) == QUERY_NODE_COLUMN) {
×
1577
        SColumnNode* pCol = (SColumnNode*)pTarget->pExpr;
×
1578
        dtype = pCol->node.resType;
×
1579
        colId = pCol->colId;
×
1580
        isPk = pCol->isPk;
×
1581
        found = true;
×
1582
      } else if (pTarget->pExpr != NULL) {
×
1583
        dtype = ((SExprNode*)pTarget->pExpr)->resType;
×
1584
        found = true;
×
1585
      }
1586
    } else if (nodeType(pFNode) == QUERY_NODE_COLUMN) {
31,048✔
1587
      SColumnNode* pCol = (SColumnNode*)pFNode;
31,048✔
1588
      dstSlot = pCol->slotId;
31,048✔
1589
      dtype = pCol->node.resType;
31,048✔
1590
      colId = pCol->colId;
31,048✔
1591
      isPk = pCol->isPk;
31,048✔
1592
      found = true;
31,048✔
1593
    }
1594

1595
    if (found) {
31,048✔
1596
      SColMatchItem c = {.needOutput = true,
31,048✔
1597
                         .colId = colId,
1598
                         .srcSlotId = dstSlot,
1599
                         .dstSlotId = dstSlot,
1600
                         .isPk = isPk,
1601
                         .dataType = dtype};
1602
      void* tmp = taosArrayPush(pMatchInfo->pList, &c);
31,048✔
1603
      if (tmp == NULL) {
31,048✔
1604
        return terrno;
×
1605
      }
1606
    }
1607
  }
1608

1609
  return TSDB_CODE_SUCCESS;
20,722✔
1610
}
1611

1612
int32_t createMergeAlignedExternalWindowOperator(SOperatorInfo* pDownstream, SPhysiNode* pNode,
63,190✔
1613
                                                 SExecTaskInfo* pTaskInfo, SOperatorInfo** ppOptrOut) {
1614
  SExternalWindowPhysiNode* pPhynode = (SExternalWindowPhysiNode*)pNode;
63,190✔
1615
  STimeWindow nonStreamExtWinRange = {.skey = INT64_MAX, .ekey = INT64_MIN};
63,190✔
1616
  int32_t code = 0;
63,190✔
1617
  int32_t lino = 0;
63,190✔
1618
  SMergeAlignedExternalWindowOperator* pMAExtW = taosMemoryCalloc(1, sizeof(SMergeAlignedExternalWindowOperator));
63,190✔
1619
  SOperatorInfo*                       pOperator = taosMemoryCalloc(1, sizeof(SOperatorInfo));
63,190✔
1620

1621
  if (pTaskInfo->pStreamRuntimeInfo != NULL){
63,190✔
1622
    pTaskInfo->pStreamRuntimeInfo->funcInfo.withExternalWindow = true;
×
1623
  }
1624
  pOperator->pPhyNode = pNode;
63,190✔
1625
  if (!pMAExtW || !pOperator) {
63,190✔
1626
    code = terrno;
×
1627
    goto _error;
×
1628
  }
1629
  initOperatorCostInfo(pOperator);
63,190✔
1630

1631
  pMAExtW->pExtW = taosMemoryCalloc(1, sizeof(SExternalWindowOperator));
63,190✔
1632
  if (!pMAExtW->pExtW) {
63,190✔
1633
    code = terrno;
×
1634
    goto _error;
×
1635
  }
1636

1637
  SExternalWindowOperator* pExtW = pMAExtW->pExtW;
63,190✔
1638
  pExtW->pTaskInfo = pTaskInfo;
63,190✔
1639
  SExprSupp* pSup = &pOperator->exprSupp;
63,190✔
1640
  pSup->hasWindowOrGroup = true;
63,190✔
1641
  pSup->hasWindow = true;
63,190✔
1642
  pMAExtW->curTs = INT64_MIN;
63,190✔
1643
  pMAExtW->lastFinalizedWinIdx = -1;
63,190✔
1644

1645
  pExtW->primaryTsIndex = ((SColumnNode*)pPhynode->window.pTspk)->slotId;
63,190✔
1646
  pExtW->mode = pPhynode->window.pFuncs ? EEXT_MODE_AGG : EEXT_MODE_SCALAR;
63,190✔
1647
  pExtW->binfo.inputTsOrder = pPhynode->window.node.inputTsOrder = TSDB_ORDER_ASC;
63,190✔
1648
  pExtW->binfo.outputTsOrder = pExtW->binfo.inputTsOrder;
63,190✔
1649
  pExtW->needGroupSort = pPhynode->needGroupSort;
63,190✔
1650
  pExtW->calcWithPartition = pPhynode->calcWithPartition;
63,190✔
1651
  pExtW->extWinSplit = pPhynode->extWinSplit;
63,190✔
1652
  pExtW->fillMode = pPhynode->extFill.mode;
63,190✔
1653
  pExtW->pFillExprs = pPhynode->extFill.pFillExprs;
63,190✔
1654
  pExtW->pFillValues = pPhynode->extFill.pFillValues;
63,190✔
1655

1656
  size_t keyBufSize = sizeof(int64_t) + sizeof(int64_t) + POINTER_BYTES;
63,190✔
1657
  initResultSizeInfo(&pOperator->resultInfo, 4096);
63,190✔
1658

1659
  int32_t num = 0;
63,190✔
1660
  SExprInfo* pExprInfo = NULL;
63,190✔
1661
  code = createExprInfo(pPhynode->window.pFuncs, NULL, &pExprInfo, &num);
63,190✔
1662
  QUERY_CHECK_CODE(code, lino, _error);
63,190✔
1663

1664
  if (pTaskInfo->execModel != OPTR_EXEC_MODEL_STREAM) {
63,190✔
1665
    code = extWinInitNonStreamWindowDataFromBlock(pPhynode, pTaskInfo, &nonStreamExtWinRange);
63,190✔
1666
    QUERY_CHECK_CODE(code, lino, _error);
63,190✔
1667
  }
1668

1669
  if (pExtW->mode == EEXT_MODE_AGG) {
63,190✔
1670
    code = initAggSup(pSup, &pExtW->aggSup, pExprInfo, num, keyBufSize, pTaskInfo->id.str, NULL,
63,190✔
1671
                      &pTaskInfo->storageAPI.functionStore);
1672
    QUERY_CHECK_CODE(code, lino, _error);
63,190✔
1673
  }
1674

1675
  code = filterInitFromNode((SNode*)pNode->pConditions, &pOperator->exprSupp.pFilterInfo, 0,
63,190✔
1676
                            pTaskInfo->pStreamRuntimeInfo);
63,190✔
1677
  QUERY_CHECK_CODE(code, lino, _error);
63,190✔
1678

1679
  code = extWinBuildFillMatchInfo(&pExtW->fillMatchInfo, pPhynode->extFill.pFillExprs);
63,190✔
1680
  QUERY_CHECK_CODE(code, lino, _error);
63,190✔
1681

1682
  SSDataBlock* pResBlock = createDataBlockFromDescNode(pPhynode->window.node.pOutputDataBlockDesc);
63,190✔
1683
  QUERY_CHECK_NULL(pResBlock, code, lino, _error, terrno);
63,190✔
1684
  initBasicInfo(&pExtW->binfo, pResBlock);
63,190✔
1685

1686
  pExtW->pWinRowIdx = taosArrayInit(4096, sizeof(int64_t));
63,190✔
1687
  TSDB_CHECK_NULL(pExtW->pWinRowIdx, code, lino, _error, terrno);
63,190✔
1688

1689
  initResultRowInfo(&pExtW->binfo.resultRowInfo);
63,190✔
1690
  code = blockDataEnsureCapacity(pExtW->binfo.pRes, pOperator->resultInfo.capacity);
63,190✔
1691
  QUERY_CHECK_CODE(code, lino, _error);
63,190✔
1692

1693
  pExtW->isMergeAlignedExtW = true;
63,190✔
1694
  
1695
  setOperatorInfo(pOperator, "MergeAlignedExternalWindowOperator", QUERY_NODE_PHYSICAL_PLAN_EXTERNAL_WINDOW, false, OP_NOT_OPENED, pMAExtW, pTaskInfo);
63,190✔
1696
  pOperator->fpSet = createOperatorFpSet(optrDummyOpenFn, mergeAlignExtWinNext, NULL,
63,190✔
1697
                                         destroyMergeAlignedExternalWindowOperator, optrDefaultBufFn, NULL,
1698
                                         optrDefaultGetNextExtFn, NULL);
1699
  setOperatorResetStateFn(pOperator, resetMergeAlignedExtWinOperator);
63,190✔
1700

1701
  code = appendDownstream(pOperator, &pDownstream, 1);
63,190✔
1702
  QUERY_CHECK_CODE(code, lino, _error);
63,190✔
1703
  *ppOptrOut = pOperator;
63,190✔
1704
  return code;
63,190✔
1705
  
1706
_error:
×
1707
  if (pMAExtW) destroyMergeAlignedExternalWindowOperator(pMAExtW);
×
1708
  destroyOperatorAndDownstreams(pOperator, &pDownstream, 1);
×
1709
  pTaskInfo->code = code;
×
1710
  return code;
×
1711
}
1712

1713
static void extWinResetResultRows(SExtWinResultRows* pRows);
1714

1715
static int32_t extWinInitExprSupp(SExprSupp* pExprSupp, SNodeList* pNodeList, SExecTaskInfo* pTaskInfo) {
2,712✔
1716
  if (pNodeList == NULL) {
2,712✔
1717
    return TSDB_CODE_SUCCESS;
×
1718
  }
1719

1720
  int32_t    num = 0;
2,712✔
1721
  SExprInfo* pExprInfo = NULL;
2,712✔
1722
  int32_t    code = createExprInfo(pNodeList, NULL, &pExprInfo, &num);
2,712✔
1723
  if (code != TSDB_CODE_SUCCESS) {
2,712✔
1724
    return code;
×
1725
  }
1726

1727
  return initExprSupp(pExprSupp, pExprInfo, num, &pTaskInfo->storageAPI.functionStore);
2,712✔
1728
}
1729

1730
static int32_t resetExternalWindowExprSupp(SExternalWindowOperator* pExtW, SExecTaskInfo* pTaskInfo,
×
1731
                                           SExternalWindowPhysiNode* pPhynode) {
1732
  int32_t code = 0, lino = 0;
×
1733
  cleanupExprSuppWithoutFilter(&pExtW->scalarSupp);
×
1734
  cleanupExprSuppWithoutFilter(&pExtW->projSupp);
×
1735

1736
  SNodeList* pNodeList = NULL;
×
1737
  if (pExtW->mode == EEXT_MODE_SCALAR) {
×
1738
    pNodeList = pPhynode->window.pProjs;
×
1739
  } else {
1740
    pNodeList = pPhynode->window.pExprs;
×
1741
  }
1742

1743
  code = extWinInitExprSupp(&pExtW->scalarSupp, pNodeList, pTaskInfo);
×
1744
  QUERY_CHECK_CODE(code, lino, _error);
×
1745
  if (pExtW->mode == EEXT_MODE_AGG) {
×
1746
    code = extWinInitExprSupp(&pExtW->projSupp, pPhynode->window.pProjs, pTaskInfo);
×
1747
    QUERY_CHECK_CODE(code, lino, _error);
×
1748
  }
1749
  QUERY_CHECK_CODE(code, lino, _error);
×
1750
  pExtW->lastGrpIdx = INT32_MAX;
×
1751
  extWinResetResultRows(&pExtW->resultRows);
×
1752
  return code;
×
1753
_error:
×
1754
  if (code != TSDB_CODE_SUCCESS) {
×
1755
    qError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(code));
×
1756
    pTaskInfo->code = code;
×
1757
  }
1758
  return code;
×
1759
}
1760

1761

1762
static int32_t resetExternalWindowOperator(SOperatorInfo* pOperator) {
×
1763
  int32_t code = 0, lino = 0;
×
1764
  SExternalWindowOperator* pExtW = pOperator->info;
×
1765
  SExecTaskInfo*           pTaskInfo = pOperator->pTaskInfo;
×
1766
  SExternalWindowPhysiNode* pPhynode = (SExternalWindowPhysiNode*)pOperator->pPhyNode;
×
1767
  pOperator->status = OP_NOT_OPENED;
×
1768

1769
  //resetBasicOperatorState(&pExtW->binfo);
1770
  initResultRowInfo(&pExtW->binfo.resultRowInfo);
×
1771

1772
  extWinRecycleBlkNode(pExtW, &pExtW->pLastBlkNode);
×
1773

1774
/*
1775
  int32_t code = blockDataEnsureCapacity(pExtW->binfo.pRes, pOperator->resultInfo.capacity);
1776
  if (code == 0) {
1777
    code = resetAggSup(&pOperator->exprSupp, &pExtW->aggSup, pTaskInfo, pPhynode->window.pFuncs, NULL,
1778
                       sizeof(int64_t) * 2 + POINTER_BYTES, pTaskInfo->id.str, pTaskInfo->streamInfo.pState,
1779
                       &pTaskInfo->storageAPI.functionStore);
1780
  }
1781
*/
1782
  TAOS_CHECK_EXIT(resetExternalWindowExprSupp(pExtW, pTaskInfo, pPhynode));
×
1783
  colDataDestroy(&pExtW->twAggSup.timeWindowData);
×
1784
  TAOS_CHECK_EXIT(initExecTimeWindowInfo(&pExtW->twAggSup.timeWindowData, &pTaskInfo->window));
×
1785

1786
  pExtW->resWinIdx = 0;
×
1787
  pExtW->lastOutputIter = 0;
×
1788
  pExtW->outWinIdx = 0;
×
1789
  pExtW->lastTGrpId = 0;
×
1790
  pExtW->lastCGrpId = 0;
×
1791
  // ownTGrpCtx==true: this operator owns the allocation; free before reset.
1792
  // ownTGrpCtx==false: borrowed pointer into pGroupCalcInfos; must not free.
1793
  if (pExtW->ownTGrpCtx && pExtW->pTGrpCtx) {
×
1794
    extWinDestroyTGrpCtx(pExtW->pTGrpCtx);
×
1795
    taosMemoryFree(pExtW->pTGrpCtx);
×
1796
  }
1797
  pExtW->pTGrpCtx = NULL;
×
1798
  pExtW->ownTGrpCtx = false;
×
1799
  pExtW->isDynWindow = false;
×
1800

1801
  extWinResetResultRows(&pExtW->resultRows);
×
1802
  
1803
  qDebug("%s ext window stat at reset, created:%" PRId64 ", destroyed:%" PRId64 ", recycled:%" PRId64 ", reused:%" PRId64 ", append:%" PRId64, 
×
1804
      pTaskInfo->id.str, pExtW->stat.resBlockCreated, pExtW->stat.resBlockDestroyed, pExtW->stat.resBlockRecycled, 
1805
      pExtW->stat.resBlockReused, pExtW->stat.resBlockAppend);
1806

1807
_exit:
×
1808

1809
  if (code) {
×
1810
    qError("%s %s failed at line %d since %s", pTaskInfo->id.str, __func__, lino, tstrerror(code));
×
1811
  }
1812
  
1813
  return code;
×
1814
}
1815

1816
static EDealRes extWinHasCountLikeFunc(SNode* pNode, void* res) {
8,278,662✔
1817
  if (QUERY_NODE_FUNCTION == nodeType(pNode)) {
8,278,662✔
1818
    SFunctionNode* pFunc = (SFunctionNode*)pNode;
3,027,802✔
1819
    if (fmIsCountLikeFunc(pFunc->funcId) || (pFunc->hasOriginalFunc && fmIsCountLikeFunc(pFunc->originalFuncId))) {
3,027,802✔
1820
      *(bool*)res = true;
976,583✔
1821
      return DEAL_RES_END;
976,138✔
1822
    }
1823
  }
1824
  return DEAL_RES_CONTINUE;
7,302,079✔
1825
}
1826

1827

1828
static int32_t extWinCreateEmptyInputBlock(SOperatorInfo* pOperator, SSDataBlock** ppBlock) {
49,892✔
1829
  int32_t code = TSDB_CODE_SUCCESS;
49,892✔
1830
  int32_t lino = 0;
49,892✔
1831
  SSDataBlock* pBlock = NULL;
49,892✔
1832
  SExternalWindowOperator* pExtW = pOperator->info;
49,892✔
1833

1834
  code = createDataBlock(&pBlock);
49,892✔
1835
  if (code) {
49,447✔
1836
    return code;
×
1837
  }
1838

1839
  pBlock->info.rows = 1;
49,447✔
1840
  pBlock->info.capacity = 0;
49,447✔
1841

1842
  SExprSupp* pSupps[] = {&pOperator->exprSupp, &pExtW->scalarSupp};
49,892✔
1843
  for (int32_t s = 0; s < 2; ++s) {
149,676✔
1844
    SExprSupp* pSupp = pSupps[s];
99,784✔
1845
    if (pSupp == NULL || pSupp->pExprInfo == NULL) {
99,784✔
1846
      continue;
49,892✔
1847
    }
1848

1849
    for (int32_t i = 0; i < pSupp->numOfExprs; ++i) {
193,170✔
1850
      SColumnInfoData colInfo = {0};
143,723✔
1851
      colInfo.hasNull = true;
143,723✔
1852
      colInfo.info.type = TSDB_DATA_TYPE_NULL;
143,723✔
1853
      colInfo.info.bytes = 1;
143,723✔
1854

1855
      SExprInfo* pOneExpr = &pSupp->pExprInfo[i];
143,723✔
1856
      for (int32_t j = 0; j < pOneExpr->base.numOfParams; ++j) {
279,110✔
1857
        SFunctParam* pFuncParam = &pOneExpr->base.pParam[j];
135,387✔
1858
        if (pFuncParam->type != FUNC_PARAM_TYPE_COLUMN) {
135,387✔
1859
          continue;
65,712✔
1860
        }
1861

1862
        int32_t slotId = pFuncParam->pCol->slotId;
69,675✔
1863
        int32_t numOfCols = taosArrayGetSize(pBlock->pDataBlock);
69,675✔
1864
        if (slotId < numOfCols) {
70,120✔
1865
          continue;
2,712✔
1866
        }
1867

1868
        code = taosArrayEnsureCap(pBlock->pDataBlock, slotId + 1);
67,408✔
1869
        QUERY_CHECK_CODE(code, lino, _end);
67,408✔
1870

1871
        for (int32_t k = numOfCols; k < slotId + 1; ++k) {
163,751✔
1872
          void* tmp = taosArrayPush(pBlock->pDataBlock, &colInfo);
96,788✔
1873
          QUERY_CHECK_NULL(tmp, code, lino, _end, terrno);
96,343✔
1874
        }
1875
      }
1876
    }
1877
  }
1878

1879
  code = blockDataEnsureCapacity(pBlock, pBlock->info.rows);
49,892✔
1880
  QUERY_CHECK_CODE(code, lino, _end);
49,892✔
1881

1882
  for (int32_t i = 0; i < blockDataGetNumOfCols(pBlock); ++i) {
146,680✔
1883
    SColumnInfoData* pColInfoData = taosArrayGet(pBlock->pDataBlock, i);
96,788✔
1884
    QUERY_CHECK_NULL(pColInfoData, code, lino, _end, terrno);
96,788✔
1885
    colDataSetNULL(pColInfoData, 0);
1886
  }
1887
  *ppBlock = pBlock;
49,447✔
1888

1889
_end:
49,447✔
1890
  if (code != TSDB_CODE_SUCCESS) {
49,447✔
1891
    blockDataDestroy(pBlock);
×
1892
    qError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(code));
×
1893
  }
1894
  return code;
49,892✔
1895
}
1896

1897

1898

1899
static int extWinTsWinCompare(const void* pLeft, const void* pRight) {
19,314,659✔
1900
  int64_t ts = *(int64_t*)pLeft;
19,314,659✔
1901
  SExtWinTimeWindow* pWin = (SExtWinTimeWindow*)pRight;
19,314,659✔
1902
  if (ts < pWin->tw.skey) {
19,314,659✔
1903
    return -1;
11,898,775✔
1904
  }
1905
  if (ts >= pWin->tw.ekey) {
7,415,884✔
1906
    return 1;
5,570,842✔
1907
  }
1908

1909
  return 0;
1,845,042✔
1910
}
1911

1912

1913
static int32_t extWinGetMultiTbWinFromTs(SOperatorInfo* pOperator, SArray* pWins, int64_t* tsCol, int64_t rowNum, int32_t* startPos) {
×
1914
  int32_t idx = taosArraySearchIdx(pWins, tsCol, extWinTsWinCompare, TD_EQ);
×
1915
  if (idx >= 0) {
×
1916
    *startPos = 0;
×
1917
    return idx;
×
1918
  }
1919

1920
  SExtWinTimeWindow* pWin = NULL;
×
1921
  int32_t w = 0;
×
1922
  for (int64_t i = 1; i < rowNum; ++i) {
×
1923
    for (; w < pWins->size; ++w) {
×
1924
      pWin = TARRAY_GET_ELEM(pWins, w);
×
1925
      if (tsCol[i] < pWin->tw.skey) {
×
1926
        break;
×
1927
      }
1928
      
1929
      if (tsCol[i] < pWin->tw.ekey) {
×
1930
        *startPos = i;
×
1931
        return w;
×
1932
      }
1933
    }
1934
  }
1935

1936
  return -1;
×
1937
}
1938

1939
static int32_t extWinGetNoOvlpWin(SOperatorInfo* pOperator, int64_t* tsCol, int32_t* startPos, SDataBlockInfo* pInfo, SExtWinTimeWindow** ppWin, int32_t* winRows) {
×
1940
  SExternalWindowOperator* pExtW = pOperator->info;
×
1941
  if ((*startPos) >= pInfo->rows) {
×
1942
    qDebug("%s %s blk rowIdx %d reach the end, size: %d, skip block", 
×
1943
        GET_TASKID(pOperator->pTaskInfo), __func__, *startPos, (int32_t)pInfo->rows);
1944
    *ppWin = NULL;
×
1945
    return TSDB_CODE_SUCCESS;
×
1946
  }
1947

1948
  SExtWinCalcGrpCtx* pCCtx = pExtW->pTGrpCtx->pCCtx;
×
1949
  
1950
  if (pCCtx->blkWinIdx < 0) {
×
1951
    pCCtx->blkWinIdx = extWinGetCurWinIdx(pOperator);
×
1952
  } else {
1953
    pCCtx->blkWinIdx++;
×
1954
  }
1955

1956
  if (pCCtx->blkWinIdx >= pCCtx->pWins->size) {
×
1957
    qDebug("%s %s ext win blk idx %d reach the end, size: %d, skip block", 
×
1958
        GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, (int32_t)pCCtx->pWins->size);
1959
    *ppWin = NULL;
×
1960
    return TSDB_CODE_SUCCESS;
×
1961
  }
1962
  
1963
  SExtWinTimeWindow* pWin = taosArrayGet(pCCtx->pWins, pCCtx->blkWinIdx);
×
1964
  if (tsCol[pInfo->rows - 1] < pWin->tw.skey) {
×
1965
    qDebug("%s %s block end ts %" PRId64 " is small than curr win %d skey %" PRId64 ", skip block", 
×
1966
        GET_TASKID(pOperator->pTaskInfo), __func__, tsCol[pInfo->rows - 1], pCCtx->blkWinIdx, pWin->tw.skey);
1967
    *ppWin = NULL;
×
1968
    return TSDB_CODE_SUCCESS;
×
1969
  }
1970

1971
  int32_t r = *startPos;
×
1972

1973
  qDebug("%s %s start to get novlp win from winIdx %d rowIdx %d", GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, r);
×
1974

1975
  // TODO handle desc order
1976
  for (; pCCtx->blkWinIdx < pCCtx->pWins->size; ++pCCtx->blkWinIdx) {
×
1977
    pWin = taosArrayGet(pCCtx->pWins, pCCtx->blkWinIdx);
×
1978
    for (; r < pInfo->rows; ++r) {
×
1979
      if (tsCol[r] < pWin->tw.skey) {
×
1980
        continue;
×
1981
      }
1982

1983
      if (tsCol[r] < pWin->tw.ekey) {
×
1984
        extWinSetCurWinIdx(pOperator, pCCtx->blkWinIdx);
×
1985
        *ppWin = pWin;
×
1986
        *startPos = r;
×
1987
        *winRows = getNumOfRowsInTimeWindow(pInfo, tsCol, r, pWin->tw.ekey - 1, binarySearchForKey, NULL, pExtW->binfo.inputTsOrder);
×
1988

1989
        qDebug("%s %s the %dth ext win TR[%" PRId64 ", %" PRId64 ") got %d rows rowStartidx %d ts[%" PRId64 ", %" PRId64 "] in blk", 
×
1990
            GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, pWin->tw.skey, pWin->tw.ekey, *winRows, r, tsCol[r], tsCol[r + *winRows - 1]);
1991
        
1992
        return TSDB_CODE_SUCCESS;
×
1993
      }
1994

1995
      if (!pOperator->pTaskInfo->pStreamRuntimeInfo && tsCol[r] >= pWin->tw.ekey) {
×
1996
        extWinSetCurWinIdx(pOperator, pCCtx->blkWinIdx);
×
1997
        *ppWin = pWin;
×
1998
        *startPos = r;
×
1999
        *winRows = getNumOfRowsInTimeWindow(pInfo, tsCol, r, pWin->tw.ekey - 1, binarySearchForKey, NULL, pExtW->binfo.inputTsOrder);
×
2000

2001
        qDebug("%s %s the %dth ext win TR[%" PRId64 ", %" PRId64 ") got %d rows rowStartidx %d ts[%" PRId64 ", %" PRId64 "] in blk",
×
2002
               GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, pWin->tw.skey, pWin->tw.ekey, *winRows, r, tsCol[r], tsCol[r + *winRows - 1]);
2003

2004
        return TSDB_CODE_SUCCESS;
×
2005
      }
2006

2007
      break;
×
2008
    }
2009

2010
    if (r == pInfo->rows) {
×
2011
      break;
×
2012
    }
2013
  }
2014

2015
  qDebug("%s %s no more ext win in block, TR[%" PRId64 ", %" PRId64 "), skip it", 
×
2016
      GET_TASKID(pOperator->pTaskInfo), __func__, tsCol[0], tsCol[pInfo->rows - 1]);
2017

2018
  *ppWin = NULL;
×
2019
  return TSDB_CODE_SUCCESS;
×
2020
}
2021

2022
static int32_t extWinGetOvlpWin(SOperatorInfo* pOperator, int64_t* tsCol, int32_t* startPos, SDataBlockInfo* pInfo, SExtWinTimeWindow** ppWin, int32_t* winRows) {
2,147,483,647✔
2023
  SExternalWindowOperator* pExtW = pOperator->info;
2,147,483,647✔
2024
  SExtWinCalcGrpCtx* pCCtx = pExtW->pTGrpCtx->pCCtx;
2,147,483,647✔
2025

2026
  if (pCCtx->blkWinIdx < 0) {
2,147,483,647✔
2027
    pCCtx->blkWinIdx = pCCtx->blkWinStartIdx;
2,065,802✔
2028
  } else {
2029
    pCCtx->blkWinIdx++;
2,147,483,647✔
2030
  }
2031

2032
  if (pCCtx->blkWinIdx >= pCCtx->pWins->size) {
2,147,483,647✔
2033
    qDebug("%s %s ext win blk idx %d reach the end, size: %d, skip block", 
905,918✔
2034
        GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, (int32_t)pCCtx->pWins->size);
2035
    *ppWin = NULL;
905,918✔
2036
    return TSDB_CODE_SUCCESS;
905,918✔
2037
  }
2038
  
2039
  SExtWinTimeWindow* pWin = taosArrayGet(pCCtx->pWins, pCCtx->blkWinIdx);
2,147,483,647✔
2040
  if (tsCol[pInfo->rows - 1] < pWin->tw.skey) {
2,147,483,647✔
2041
    qDebug("%s %s block end ts %" PRId64 " is small than curr win %d skey %" PRId64 ", skip block", 
1,021,727✔
2042
        GET_TASKID(pOperator->pTaskInfo), __func__, tsCol[pInfo->rows - 1], pCCtx->blkWinIdx, pWin->tw.skey);
2043
    *ppWin = NULL;
1,021,727✔
2044
    return TSDB_CODE_SUCCESS;
1,021,727✔
2045
  }
2046

2047
  int64_t r = 0;
2,147,483,647✔
2048

2049
  qDebug("%s %s start to get ovlp win from winIdx %d rowIdx %d", GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, pCCtx->blkRowStartIdx);
2,147,483,647✔
2050
  
2051
  // TODO handle desc order
2052
  for (; pCCtx->blkWinIdx < pCCtx->pWins->size; ++pCCtx->blkWinIdx) {
2,147,483,647✔
2053
    pWin = taosArrayGet(pCCtx->pWins, pCCtx->blkWinIdx);
2,147,483,647✔
2054
    for (r = pCCtx->blkRowStartIdx; r < pInfo->rows; ++r) {
2,147,483,647✔
2055
      if (tsCol[r] < pWin->tw.skey) {
2,147,483,647✔
2056
        pCCtx->blkRowStartIdx = r + 1;
2,147,483,647✔
2057
        continue;
2,147,483,647✔
2058
      }
2059

2060
      if (tsCol[r] < pWin->tw.ekey) {
2,147,483,647✔
2061
        extWinSetCurWinIdx(pOperator, pCCtx->blkWinIdx);
2,147,483,647✔
2062
        *ppWin = pWin;
2,147,483,647✔
2063
        *startPos = r;
2,147,483,647✔
2064
        *winRows = getNumOfRowsInTimeWindow(pInfo, tsCol, r, pWin->tw.ekey - 1, binarySearchForKey, NULL, pExtW->binfo.inputTsOrder);
2,147,483,647✔
2065

2066
        qDebug("%s %s the %dth ext win TR[%" PRId64 ", %" PRId64 ") got %d rows rowStartidx %d ts[%" PRId64 ", %" PRId64 "] in blk", 
2,147,483,647✔
2067
            GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, pWin->tw.skey, pWin->tw.ekey, *winRows, (int32_t)r, tsCol[r], tsCol[r + *winRows - 1]);
2068
        
2069
        if ((r + *winRows) < pInfo->rows) {
2,147,483,647✔
2070
          pCCtx->blkWinStartIdx = pCCtx->blkWinIdx + 1;
2,147,483,647✔
2071
          pCCtx->blkWinStartSet = true;
2,147,483,647✔
2072
        }
2073
        
2074
        return TSDB_CODE_SUCCESS;
2,147,483,647✔
2075
      }
2076

2077
      break;
335,398,142✔
2078
    }
2079

2080
    if (r >= pInfo->rows) {
335,510,282✔
2081
      if (!pCCtx->blkWinStartSet) {
112,140✔
2082
        pCCtx->blkWinStartIdx = pCCtx->blkWinIdx;
112,140✔
2083
      }
2084
      
2085
      break;
112,140✔
2086
    }
2087
  }
2088

2089
  qDebug("%s %s no more ext win in block, TR[%" PRId64 ", %" PRId64 "), skip it", 
138,157✔
2090
      GET_TASKID(pOperator->pTaskInfo), __func__, tsCol[0], tsCol[pInfo->rows - 1]);
2091

2092
  *ppWin = NULL;
138,157✔
2093
  return TSDB_CODE_SUCCESS;
138,157✔
2094
}
2095

2096

2097
static int32_t extWinGetMultiTbNoOvlpWin(SOperatorInfo* pOperator, int64_t* tsCol, int32_t* startPos, SDataBlockInfo* pInfo, SExtWinTimeWindow** ppWin, int32_t* winRows) {
×
2098
  SExternalWindowOperator* pExtW = pOperator->info;
×
2099
  SExtWinCalcGrpCtx* pCCtx = pExtW->pTGrpCtx->pCCtx;
×
2100

2101
  if ((*startPos) >= pInfo->rows) {
×
2102
    qDebug("%s %s blk rowIdx %d reach the end, size: %d, skip block", 
×
2103
        GET_TASKID(pOperator->pTaskInfo), __func__, *startPos, (int32_t)pInfo->rows);
2104
    *ppWin = NULL;
×
2105
    return TSDB_CODE_SUCCESS;
×
2106
  }
2107
  
2108
  if (pCCtx->blkWinIdx < 0) {
×
2109
    pCCtx->blkWinIdx = extWinGetMultiTbWinFromTs(pOperator, pCCtx->pWins, tsCol, pInfo->rows, startPos);
×
2110
    if (pCCtx->blkWinIdx < 0) {
×
2111
      qDebug("%s %s blk TR[%" PRId64 ", %" PRId64 ") not in any win, skip block", 
×
2112
          GET_TASKID(pOperator->pTaskInfo), __func__, tsCol[0], tsCol[pInfo->rows - 1]);
2113
      *ppWin = NULL;
×
2114
      return TSDB_CODE_SUCCESS;
×
2115
    }
2116

2117
    extWinSetCurWinIdx(pOperator, pCCtx->blkWinIdx);
×
2118
    *ppWin = taosArrayGet(pCCtx->pWins, pCCtx->blkWinIdx);
×
2119
    *winRows = getNumOfRowsInTimeWindow(pInfo, tsCol, *startPos, (*ppWin)->tw.ekey - 1, binarySearchForKey, NULL, pExtW->binfo.inputTsOrder);
×
2120

2121
    qDebug("%s %s the %dth ext win TR[%" PRId64 ", %" PRId64 ") got %d rows rowStartidx %d ts[%" PRId64 ", %" PRId64 "] in blk", 
×
2122
        GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, (*ppWin)->tw.skey, (*ppWin)->tw.ekey, *winRows, *startPos, tsCol[*startPos], tsCol[*startPos + *winRows - 1]);
2123
    
2124
    return TSDB_CODE_SUCCESS;
×
2125
  } else {
2126
    pCCtx->blkWinIdx++;
×
2127
  }
2128

2129
  if (pCCtx->blkWinIdx >= pCCtx->pWins->size) {
×
2130
    qDebug("%s %s ext win blk idx %d reach the end, size: %d, skip block", 
×
2131
        GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, (int32_t)pCCtx->pWins->size);
2132
    *ppWin = NULL;
×
2133
    return TSDB_CODE_SUCCESS;
×
2134
  }
2135
  
2136
  SExtWinTimeWindow* pWin = taosArrayGet(pCCtx->pWins, pCCtx->blkWinIdx);
×
2137
  if (tsCol[pInfo->rows - 1] < pWin->tw.skey) {
×
2138
    qDebug("%s %s block end ts %" PRId64 " is small than curr win %d skey %" PRId64 ", skip block", 
×
2139
        GET_TASKID(pOperator->pTaskInfo), __func__, tsCol[pInfo->rows - 1], pCCtx->blkWinIdx, pWin->tw.skey);
2140
    *ppWin = NULL;
×
2141
    return TSDB_CODE_SUCCESS;
×
2142
  }
2143

2144
  int32_t r = *startPos;
×
2145

2146
  qDebug("%s %s start to get mnovlp win from winIdx %d rowIdx %d", GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, r);
×
2147

2148
  // TODO handle desc order
2149
  for (; pCCtx->blkWinIdx < pCCtx->pWins->size; ++pCCtx->blkWinIdx) {
×
2150
    pWin = taosArrayGet(pCCtx->pWins, pCCtx->blkWinIdx);
×
2151
    for (; r < pInfo->rows; ++r) {
×
2152
      if (tsCol[r] < pWin->tw.skey) {
×
2153
        continue;
×
2154
      }
2155

2156
      if (tsCol[r] < pWin->tw.ekey) {
×
2157
        extWinSetCurWinIdx(pOperator, pCCtx->blkWinIdx);
×
2158
        *ppWin = pWin;
×
2159
        *startPos = r;
×
2160
        *winRows = getNumOfRowsInTimeWindow(pInfo, tsCol, r, pWin->tw.ekey - 1, binarySearchForKey, NULL, pExtW->binfo.inputTsOrder);
×
2161

2162
        qDebug("%s %s the %dth ext win TR[%" PRId64 ", %" PRId64 ") got %d rows rowStartidx %d ts[%" PRId64 ", %" PRId64 "] in blk", 
×
2163
            GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, pWin->tw.skey, pWin->tw.ekey, *winRows, r, tsCol[r], tsCol[r + *winRows - 1]);
2164
        
2165
        return TSDB_CODE_SUCCESS;
×
2166
      }
2167

2168
      if (!pOperator->pTaskInfo->pStreamRuntimeInfo && tsCol[r] >= pWin->tw.ekey) {
×
2169
        extWinSetCurWinIdx(pOperator, pCCtx->blkWinIdx);
×
2170
        *ppWin = pWin;
×
2171
        *startPos = r;
×
2172
        *winRows = getNumOfRowsInTimeWindow(pInfo, tsCol, r, pWin->tw.ekey - 1, binarySearchForKey, NULL, pExtW->binfo.inputTsOrder);
×
2173

2174
        qDebug("%s %s the %dth ext win TR[%" PRId64 ", %" PRId64 ") got %d rows rowStartidx %d ts[%" PRId64 ", %" PRId64 "] in blk",
×
2175
               GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, pWin->tw.skey, pWin->tw.ekey, *winRows, r, tsCol[r], tsCol[r + *winRows - 1]);
2176

2177
        return TSDB_CODE_SUCCESS;
×
2178
      }
2179

2180
      break;
×
2181
    }
2182

2183
    if (r == pInfo->rows) {
×
2184
      break;
×
2185
    }
2186
  }
2187

2188
  qDebug("%s %s no more ext win in block, TR[%" PRId64 ", %" PRId64 "), skip it", 
×
2189
      GET_TASKID(pOperator->pTaskInfo), __func__, tsCol[0], tsCol[pInfo->rows - 1]);
2190

2191
  *ppWin = NULL;
×
2192
  return TSDB_CODE_SUCCESS;
×
2193
}
2194

2195
static int32_t extWinGetFirstWinFromTs(SOperatorInfo* pOperator, SArray* pWins, int64_t* tsCol,
2,030,438✔
2196
                                       int64_t rowNum, int32_t* startPos) {
2197
  SExtWinTimeWindow* pWin = NULL;
2,030,438✔
2198
  int32_t            idx = taosArraySearchIdx(pWins, tsCol, extWinTsWinCompare, TD_EQ);
2,030,438✔
2199
  if (idx >= 0) {
2,030,438✔
2200
    for (int i = idx - 1; i >= 0; --i) {
1,845,042✔
2201
      pWin = TARRAY_GET_ELEM(pWins, i);
872,754✔
2202
      if (extWinTsWinCompare(tsCol, pWin) == 0) {
872,754✔
2203
        idx = i;
2,225✔
2204
      } else {
2205
        break;
870,529✔
2206
      }
2207
    }
2208
    *startPos = 0;
1,842,817✔
2209
    return idx;
1,842,817✔
2210
  }
2211

2212
  pWin = NULL;
187,621✔
2213
  int32_t w = 0;
187,621✔
2214
  for (int64_t i = 1; i < rowNum; ++i) {
65,139,700✔
2215
    for (; w < pWins->size; ++w) {
351,486,186✔
2216
      pWin = TARRAY_GET_ELEM(pWins, w);
351,482,626✔
2217
      if (tsCol[i] < pWin->tw.skey) {
351,482,626✔
2218
        break;
64,948,519✔
2219
      }
2220

2221
      if (tsCol[i] < pWin->tw.ekey) {
286,534,107✔
2222
        *startPos = i;
162,428✔
2223
        return w;
162,428✔
2224
      }
2225
    }
2226
  }
2227

2228
  return -1;
25,193✔
2229
}
2230

2231
static int32_t extWinGetMultiTbOvlpWin(SOperatorInfo* pOperator, int64_t* tsCol, int32_t* startPos, SDataBlockInfo* pInfo, SExtWinTimeWindow** ppWin, int32_t* winRows) {
1,951,275,673✔
2232
  SExternalWindowOperator* pExtW = pOperator->info;
1,951,275,673✔
2233
  SExtWinCalcGrpCtx* pCCtx = pExtW->pTGrpCtx->pCCtx;
1,951,277,453✔
2234

2235
  if (pCCtx->blkWinIdx < 0) {
1,951,278,343✔
2236
    pCCtx->blkWinIdx = extWinGetFirstWinFromTs(pOperator, pCCtx->pWins, tsCol, pInfo->rows, startPos);
2,030,438✔
2237
    if (pCCtx->blkWinIdx < 0) {
2,030,438✔
2238
      qDebug("%s %s blk TR[%" PRId64 ", %" PRId64 ") not in any win, skip block", 
25,193✔
2239
          GET_TASKID(pOperator->pTaskInfo), __func__, tsCol[0], tsCol[pInfo->rows - 1]);
2240
      *ppWin = NULL;
25,193✔
2241
      return TSDB_CODE_SUCCESS;
25,193✔
2242
    }
2243

2244
    extWinSetCurWinIdx(pOperator, pCCtx->blkWinIdx);
2,005,245✔
2245
    *ppWin = taosArrayGet(pCCtx->pWins, pCCtx->blkWinIdx);
2,005,245✔
2246
    *winRows = getNumOfRowsInTimeWindow(pInfo, tsCol, *startPos, (*ppWin)->tw.ekey - 1, binarySearchForKey, NULL, pExtW->binfo.inputTsOrder);
2,005,245✔
2247
    
2248
    qDebug("%s %s the %dth ext win TR[%" PRId64 ", %" PRId64 ") got %d rows rowStartidx %d ts[%" PRId64 ", %" PRId64 "] in blk", 
2,005,245✔
2249
        GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, (*ppWin)->tw.skey, (*ppWin)->tw.ekey, *winRows, *startPos, tsCol[*startPos], tsCol[*startPos + *winRows - 1]);
2250
    
2251
    return TSDB_CODE_SUCCESS;
2,005,245✔
2252
  } else {
2253
    pCCtx->blkWinIdx++;
1,949,245,235✔
2254
  }
2255

2256
  if (pCCtx->blkWinIdx >= pCCtx->pWins->size) {
1,949,246,570✔
2257
    qDebug("%s %s ext win blk idx %d reach the end, size: %d, skip block", 
838,855✔
2258
        GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, (int32_t)pCCtx->pWins->size);
2259
    *ppWin = NULL;
838,855✔
2260
    return TSDB_CODE_SUCCESS;
838,855✔
2261
  }
2262
  
2263
  SExtWinTimeWindow* pWin = taosArrayGet(pCCtx->pWins, pCCtx->blkWinIdx);
1,948,407,270✔
2264
  if (tsCol[pInfo->rows - 1] < pWin->tw.skey) {
1,948,408,605✔
2265
    qDebug("%s %s block end ts %" PRId64 " is small than curr win %d skey %" PRId64 ", skip block", 
1,148,909✔
2266
        GET_TASKID(pOperator->pTaskInfo), __func__, tsCol[pInfo->rows - 1], pCCtx->blkWinIdx, pWin->tw.skey);
2267
    *ppWin = NULL;
1,148,909✔
2268
    return TSDB_CODE_SUCCESS;
1,148,909✔
2269
  }
2270

2271
  int64_t r = 0;
1,947,258,806✔
2272

2273
  qDebug("%s %s start to get movlp win from winIdx %d rowIdx %d", GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, pCCtx->blkRowStartIdx);
1,947,258,806✔
2274

2275
  // TODO handle desc order
2276
  for (; pCCtx->blkWinIdx < pCCtx->pWins->size; ++pCCtx->blkWinIdx) {
2,147,483,647✔
2277
    pWin = taosArrayGet(pCCtx->pWins, pCCtx->blkWinIdx);
2,147,483,647✔
2278
    for (r = pCCtx->blkRowStartIdx; r < pInfo->rows; ++r) {
2,147,483,647✔
2279
      if (tsCol[r] < pWin->tw.skey) {
2,147,483,647✔
2280
        pCCtx->blkRowStartIdx = r + 1;
2,147,483,647✔
2281
        continue;
2,147,483,647✔
2282
      }
2283

2284
      if (tsCol[r] < pWin->tw.ekey) {
2,147,483,647✔
2285
        extWinSetCurWinIdx(pOperator, pCCtx->blkWinIdx);
1,947,242,660✔
2286
        *ppWin = pWin;
1,947,242,215✔
2287
        *startPos = r;
1,947,242,660✔
2288
        *winRows = getNumOfRowsInTimeWindow(pInfo, tsCol, r, pWin->tw.ekey - 1, binarySearchForKey, NULL, pExtW->binfo.inputTsOrder);
1,947,242,660✔
2289

2290
        qDebug("%s %s the %dth ext win TR[%" PRId64 ", %" PRId64 ") got %d rows rowStartidx %d ts[%" PRId64 ", %" PRId64 "] in blk", 
1,947,242,660✔
2291
            GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, pWin->tw.skey, pWin->tw.ekey, *winRows, (int32_t)r, tsCol[r], tsCol[r + *winRows - 1]);
2292
        
2293
        return TSDB_CODE_SUCCESS;
1,947,242,660✔
2294
      }
2295

2296
      break;
212,710,379✔
2297
    }
2298

2299
    if (r >= pInfo->rows) {
212,712,724✔
2300
      break;
2,345✔
2301
    }
2302
  }
2303

2304
  qDebug("%s %s no more ext win in block, TR[%" PRId64 ", %" PRId64 "), skip it", 
17,481✔
2305
      GET_TASKID(pOperator->pTaskInfo), __func__, tsCol[0], tsCol[pInfo->rows - 1]);
2306

2307
  *ppWin = NULL;
17,481✔
2308
  return TSDB_CODE_SUCCESS;
17,481✔
2309
}
2310

2311

2312
static int32_t extWinGetResultRow(SExecTaskInfo* pTaskInfo, SExternalWindowOperator* pExtW, int32_t winIdx, int32_t resultRowSize, SResultRow** ppRes) {
2,147,483,647✔
2313
  int32_t code = 0, lino = 0;
2,147,483,647✔
2314
  SExtWinResultRows* pRows = &pExtW->resultRows;
2,147,483,647✔
2315
  while (true) {
2316
    if (pRows->pResultRows[pRows->resRowsIdx] && pRows->resRowIdx < pRows->resRowSize) {
2,147,483,647✔
2317
      break;
2,147,483,647✔
2318
    }
2319

2320
    if (NULL == pRows->pResultRows[pRows->resRowsIdx]) {
1,429,199✔
2321
      pRows->pResultRows[pRows->resRowsIdx] = taosMemoryMalloc(pRows->resRowSize * resultRowSize);
1,403,120✔
2322
      TSDB_CHECK_NULL(pRows->pResultRows[pRows->resRowsIdx], code, lino, _exit, terrno);
1,403,120✔
2323
      pRows->resRowAllcNum += pRows->resRowSize;
1,403,120✔
2324
      continue;
1,403,120✔
2325
    }
2326

2327
    // pRows->resRowIdx >= pRows->resRowSize
2328
    
2329
    pRows->resRowIdx = 0;
26,079✔
2330
    pRows->resRowsIdx++;
26,079✔
2331

2332
    if (pRows->resRowsIdx >= pRows->resRowsSize) {
26,079✔
2333
      int32_t oldSize = pRows->resRowsSize;
×
2334
      pRows->resRowsSize += EXT_WIN_RES_ROWS_ALLOC_SIZE;
×
2335
      pRows->pResultRows = taosMemoryRealloc(pRows->pResultRows, pRows->resRowsSize * POINTER_BYTES);
×
2336
      TSDB_CHECK_NULL(pRows->pResultRows, code, lino, _exit, terrno);
×
2337
      memset(pRows->pResultRows + oldSize, 0, (pRows->resRowsSize - oldSize) * POINTER_BYTES);
×
2338
    }
2339
  }
2340

2341
  pExtW->pTGrpCtx->pCCtx->outWinTotalNum++;
2,147,483,647✔
2342
  //TSDB_CHECK_NULL(taosArrayPush(pExtW->pTGrpCtx->outWinBufIdx, &winIdx), code, lino, _exit, terrno);
2343

2344
  *ppRes = (SResultRow*)((char*)pRows->pResultRows[pRows->resRowsIdx] + pRows->resRowIdx++ * resultRowSize);
2,147,483,647✔
2345

2346
_exit:
2,147,483,647✔
2347

2348
  if (code) {
2,147,483,647✔
2349
    qError("%s %s failed at line %d since %s", GET_TASKID(pTaskInfo), __func__, lino, tstrerror(code));
×
2350
  }
2351

2352
  return code;
2,147,483,647✔
2353
}
2354

2355
static FORCE_INLINE SResultRow* extWinGetResultRowByIdx(SExternalWindowOperator* pExtW, int32_t resWinIdx,
2356
                                                         int32_t resultRowSize) {
2357
  SExtWinResultRows* pRows = &pExtW->resultRows;
2,147,483,647✔
2358
  int32_t            resRowsIdx = resWinIdx / pRows->resRowSize;
2,147,483,647✔
2359
  if (resRowsIdx >= pRows->resRowsSize || pRows->pResultRows[resRowsIdx] == NULL) {
2,147,483,647✔
2360
    return NULL;
×
2361
  }
2362

2363
  return (SResultRow*)((char*)pRows->pResultRows[resRowsIdx] + (resWinIdx % pRows->resRowSize) * resultRowSize);
2,147,483,647✔
2364
}
2365

2366
static FORCE_INLINE bool extWinRowHasSourceData(const SResultRow* pRow) {
2367
  return pRow != NULL && pRow->nOrigRows > 0;
187,658✔
2368
}
2369

2370
static bool extWinHasNaturalRows(SExternalWindowOperator* pExtW, SExtWinCalcGrpCtx* pCCtx) {
81,434✔
2371
  if (pCCtx == NULL || pCCtx->pWins == NULL) {
81,434✔
2372
    return false;
×
2373
  }
2374

2375
  for (int32_t i = 0; i < taosArrayGetSize(pCCtx->pWins); ++i) {
154,662✔
2376
    SExtWinTimeWindow* pWin = TARRAY_GET_ELEM(pCCtx->pWins, i);
146,638✔
2377
    if (pWin->resWinIdx < 0) {
146,638✔
2378
      continue;
73,228✔
2379
    }
2380

2381
    SResultRow* pRow = extWinGetResultRowByIdx(pExtW, pWin->resWinIdx, pExtW->aggSup.resultRowSize);
146,820✔
2382
    if (extWinRowHasSourceData(pRow)) {
73,410✔
2383
      return true;
73,410✔
2384
    }
2385
  }
2386

2387
  return false;
8,024✔
2388
}
2389

2390
static bool extWinShouldEmitAllWindows(SExternalWindowOperator* pExtW, SExtWinCalcGrpCtx* pCCtx) {
92,282✔
2391
  if (pExtW->isDynWindow) {
92,282✔
2392
    return true;
×
2393
  }
2394

2395
  switch (pExtW->fillMode) {
92,282✔
2396
    case FILL_MODE_NONE:
×
2397
      return false;
×
2398
    case FILL_MODE_NULL_F:
10,848✔
2399
    case FILL_MODE_VALUE_F:
2400
      return true;
10,848✔
2401
    case FILL_MODE_PREV:
81,434✔
2402
    case FILL_MODE_NEXT:
2403
    case FILL_MODE_NULL:
2404
    case FILL_MODE_VALUE:
2405
      return extWinHasNaturalRows(pExtW, pCCtx);
81,434✔
2406
    default:
×
2407
      return false;
×
2408
  }
2409
}
2410

2411
static FORCE_INLINE bool extWinUsesValueFillMode(EFillMode fillMode) {
2412
  return fillMode == FILL_MODE_VALUE || fillMode == FILL_MODE_VALUE_F;
94,920✔
2413
}
2414

2415
static SResultRow* extWinFindAdjacentFillRow(SExternalWindowOperator* pExtW, SExtWinCalcGrpCtx* pCCtx, int32_t winIdx,
24,860✔
2416
                                             bool next) {
2417
  int32_t start = next ? (winIdx + 1) : (winIdx - 1);
24,860✔
2418
  int32_t end = next ? taosArrayGetSize(pCCtx->pWins) : -1;
24,860✔
2419
  int32_t step = next ? 1 : -1;
24,860✔
2420

2421
  for (int32_t i = start; i != end; i += step) {
38,420✔
2422
    SExtWinTimeWindow* pWin = TARRAY_GET_ELEM(pCCtx->pWins, i);
28,024✔
2423
    if (pWin->resWinIdx < 0) {
28,024✔
2424
      continue;
13,560✔
2425
    }
2426

2427
    SResultRow* pRow = extWinGetResultRowByIdx(pExtW, pWin->resWinIdx, pExtW->aggSup.resultRowSize);
28,928✔
2428
    if (extWinRowHasSourceData(pRow)) {
14,464✔
2429
      return pRow;
14,464✔
2430
    }
2431
  }
2432

2433
  return NULL;
10,396✔
2434
}
2435

2436
static int32_t extWinApplyValueFill(SExternalWindowOperator* pExtW, SSDataBlock* pBlock, int32_t startRow,
49,268✔
2437
                                    int32_t numOfRows) {
2438
  if (pExtW->pFillValues == NULL || pExtW->fillMatchInfo.pList == NULL) {
49,268✔
2439
    return TSDB_CODE_SUCCESS;
×
2440
  }
2441

2442
  SNodeListNode* pValueList = (SNodeListNode*)pExtW->pFillValues;
49,268✔
2443
  int32_t matchNum = taosArrayGetSize(pExtW->fillMatchInfo.pList);
49,268✔
2444
  for (int32_t i = 0; i < matchNum; ++i) {
110,740✔
2445
    SColMatchItem* pItem = taosArrayGet(pExtW->fillMatchInfo.pList, i);
61,472✔
2446
    if (pItem == NULL || !pItem->needOutput || pItem->dstSlotId < 0) {
61,472✔
2447
      continue;
×
2448
    }
2449

2450
    SValueNode* pValue = (SValueNode*)nodesListGetNode(pValueList->pNodeList, i);
61,472✔
2451
    if (pValue == NULL) {
61,472✔
2452
      continue;
×
2453
    }
2454

2455
    SColumnInfoData* pCol = taosArrayGet(pBlock->pDataBlock, pItem->dstSlotId);
61,472✔
2456
    if (pCol == NULL) {
61,472✔
2457
      return terrno;
×
2458
    }
2459

2460
    void* pData = nodesGetValueFromNode(pValue);
61,472✔
2461
    for (int32_t row = 0; row < numOfRows; ++row) {
122,944✔
2462
      int32_t code = colDataSetValOrCover(pCol, startRow + row, pData, pValue->isNull);
61,472✔
2463
      if (code != TSDB_CODE_SUCCESS) {
61,472✔
2464
        return code;
×
2465
      }
2466
    }
2467
  }
2468

2469
  return TSDB_CODE_SUCCESS;
49,268✔
2470
}
2471

2472
static int32_t extWinApplyAggPostProjection(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW,
2,147,483,647✔
2473
                                            SSDataBlock* pBlock, int32_t startRow, int32_t numOfRows) {
2474
  if (pExtW->projSupp.pExprInfo == NULL || pBlock == NULL || pBlock->info.rows <= 0 || numOfRows <= 0) {
2,147,483,647✔
2475
    return TSDB_CODE_SUCCESS;
2,147,483,647✔
2476
  }
2477

2478
  int32_t code = TSDB_CODE_SUCCESS;
8,136✔
2479
  int32_t lino = 0;
8,136✔
2480

2481
  if (startRow < 0 || startRow + numOfRows > pBlock->info.rows) {
8,136✔
2482
    return TSDB_CODE_INVALID_PARA;
×
2483
  }
2484

2485
  // Lazy-init a reusable block with the same schema as pBlock to avoid
2486
  // allocating a new block on every call (which happens once per filled row).
2487
  if (pExtW->pProjTmpBlock == NULL) {
8,136✔
2488
    TAOS_CHECK_EXIT(createOneDataBlock(pBlock, false, &pExtW->pProjTmpBlock));
2,712✔
2489
  }
2490
  blockDataCleanup(pExtW->pProjTmpBlock);
8,136✔
2491
  TAOS_CHECK_EXIT(blockDataEnsureCapacity(pExtW->pProjTmpBlock, numOfRows));
8,136✔
2492
  TAOS_CHECK_EXIT(blockDataMergeNRows(pExtW->pProjTmpBlock, pBlock, startRow, numOfRows));
8,136✔
2493

2494
  SSDataBlock* pSlice = pExtW->pProjTmpBlock;
8,136✔
2495
  TAOS_CHECK_EXIT(projectApplyFunctions(pExtW->projSupp.pExprInfo, pSlice, pSlice, pExtW->projSupp.pCtx,
8,136✔
2496
                                        pExtW->projSupp.numOfExprs, NULL,
2497
                                        GET_STM_RTINFO(pOperator->pTaskInfo), pOperator->pTaskInfo));
2498

2499
  int32_t numOfCols = taosArrayGetSize(pBlock->pDataBlock);
8,136✔
2500
  // TODO(perf): only copy back the slots actually written by projSupp, not all columns.
2501
  // Passthrough columns (_wstart, group keys, etc.) are unchanged in pSlice and
2502
  // do not need to be re-written to pBlock.
2503
  for (int32_t i = 0; i < numOfCols; ++i) {
48,816✔
2504
    SColumnInfoData* pDstCol = taosArrayGet(pBlock->pDataBlock, i);
40,680✔
2505
    SColumnInfoData* pSrcCol = taosArrayGet(pSlice->pDataBlock, i);
40,680✔
2506
    if (pDstCol == NULL || pSrcCol == NULL) {
40,680✔
2507
      continue;
×
2508
    }
2509

2510
    TAOS_CHECK_EXIT(colDataAssignNRows(pDstCol, startRow, pSrcCol, 0, numOfRows));
40,680✔
2511
  }
2512

2513
_exit:
8,136✔
2514
  return code;
8,136✔
2515
}
2516

2517
static int32_t extWinAppendAggFilledRow(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW,
94,920✔
2518
                                        SExtWinCalcGrpCtx* pCCtx, SExtWinTimeWindow* pWin, SResultRow* pSrcRow) {
2519
  int32_t         code = TSDB_CODE_SUCCESS;
94,920✔
2520
  int32_t         lino = 0;
94,920✔
2521
  SSDataBlock*    pBlock = pExtW->binfo.pRes;
94,920✔
2522
  SExecTaskInfo*  pTaskInfo = pOperator->pTaskInfo;
94,920✔
2523
  SExprInfo*      pExprInfo = pOperator->exprSupp.pExprInfo;
94,920✔
2524
  int32_t         numOfExprs = pOperator->exprSupp.numOfExprs;
94,920✔
2525
  int32_t*        rowEntryOffset = pOperator->exprSupp.rowEntryInfoOffset;
94,920✔
2526
  SqlFunctionCtx* pCtx = pOperator->exprSupp.pCtx;
94,920✔
2527

2528
  // Only take the NULL path when there is no source row at all.
2529
  // Do NOT check pSrcRow->numOfRows here: for NEXT fill, the source row is
2530
  // from a forward window whose doUpdateNumOfRows hasn't been called yet,
2531
  // so numOfRows is still 0 even though the row has valid aggregation results.
2532
  if (pSrcRow == NULL) {
94,920✔
2533
    int32_t startRow = pBlock->info.rows;
80,456✔
2534
    if (pBlock->info.rows + 1 > pBlock->info.capacity) {
80,456✔
2535
      TAOS_CHECK_EXIT(blockDataEnsureCapacity(pBlock, pBlock->info.rows + 1));
31,640✔
2536
    }
2537
    for (int32_t j = 0; j < taosArrayGetSize(pBlock->pDataBlock); ++j) {
386,008✔
2538
      SColumnInfoData* pColInfo = taosArrayGet(pBlock->pDataBlock, j);
305,552✔
2539
      if (pColInfo != NULL) {
305,552✔
2540
        colDataSetNULL(pColInfo, pBlock->info.rows);
305,552✔
2541
      }
2542
    }
2543

2544
    // Patch pseudo columns (_wstart/_twstart, _wend/_twend) with original window timestamps.
2545
    // Also patch _group_key columns with values from any existing result row in this partition.
2546
    SResultRow* pAnyRow = NULL;
80,456✔
2547
    int32_t     groupKeyIdx = 0;
80,456✔
2548
    for (int32_t j = 0; j < numOfExprs; ++j) {
299,224✔
2549
      int32_t slotId = pExprInfo[j].base.resSchema.slotId;
218,768✔
2550
      SColumnInfoData* pColInfo = taosArrayGet(pBlock->pDataBlock, slotId);
218,768✔
2551
      if (pColInfo == NULL) continue;
218,768✔
2552

2553
      if (pCtx[j].isPseudoFunc) {
218,768✔
2554
        int32_t funcType = pCtx[j].pExpr->pExpr->_function.functionType;
103,508✔
2555
        if (funcType == FUNCTION_TYPE_WSTART || funcType == FUNCTION_TYPE_TWSTART) {
103,508✔
2556
          TAOS_CHECK_EXIT(colDataSetVal(pColInfo, pBlock->info.rows, (const char*)&pWin->tw.skey, false));
79,552✔
2557
        } else if (funcType == FUNCTION_TYPE_WEND || funcType == FUNCTION_TYPE_TWEND) {
23,956✔
2558
          TAOS_CHECK_EXIT(colDataSetVal(pColInfo, pBlock->info.rows, (const char*)&pWin->tw.ekey, false));
3,616✔
2559
        } else if (funcType == FUNCTION_TYPE_WDURATION || funcType == FUNCTION_TYPE_TWDURATION) {
20,340✔
2560
          int64_t dur = pWin->tw.ekey - pWin->tw.skey;
×
2561
          TAOS_CHECK_EXIT(colDataSetVal(pColInfo, pBlock->info.rows, (const char*)&dur, false));
×
2562
        } else if (funcType == FUNCTION_TYPE_EXTERNAL_WINDOW_COLUMN && pTaskInfo->pStreamRuntimeInfo != NULL) {
20,340✔
2563
          // w.mark is window metadata — always show the current window's own value.
2564
          SStreamRuntimeFuncInfo* pStreamInfo = &pTaskInfo->pStreamRuntimeInfo->funcInfo;
20,340✔
2565
          SNodeList* pParamList = pCtx[j].pExpr->base.pParamList;
20,340✔
2566
          if (pParamList != NULL && LIST_LENGTH(pParamList) >= 2) {
20,340✔
2567
            SNode* pSecond = nodesListGetNode(pParamList, 1);
20,340✔
2568
            if (pSecond != NULL && nodeType(pSecond) == QUERY_NODE_VALUE) {
20,340✔
2569
              int32_t placeholderNo = ((SValueNode*)pSecond)->placeholderNo;
20,340✔
2570
              SSTriggerCalcParam* pCurParam = taosArrayGet(pStreamInfo->pStreamPesudoFuncVals, pCCtx->outWinIdx);
20,340✔
2571
              if (pCurParam != NULL && pCurParam->pExternalWindowData != NULL &&
20,340✔
2572
                  placeholderNo >= 0 && placeholderNo < (int32_t)taosArrayGetSize(pCurParam->pExternalWindowData)) {
20,340✔
2573
                SStreamGroupValue* pVal = taosArrayGet(pCurParam->pExternalWindowData, placeholderNo);
20,340✔
2574
                if (pVal != NULL) {
20,340✔
2575
                  if (pVal->isNull) {
20,340✔
2576
                    colDataSetNULL(pColInfo, pBlock->info.rows);
×
2577
                  } else if (IS_VAR_DATA_TYPE(pVal->data.type) || pVal->data.type == TSDB_DATA_TYPE_DECIMAL) {
20,340✔
2578
                    TAOS_CHECK_EXIT(colDataSetVal(pColInfo, pBlock->info.rows, (const char*)pVal->data.pData, false));
2,712✔
2579
                  } else {
2580
                    TAOS_CHECK_EXIT(colDataSetVal(pColInfo, pBlock->info.rows, (const char*)&pVal->data.val, false));
17,628✔
2581
                  }
2582
                }
2583
              }
2584
            }
2585
          }
2586
        }
2587
      } else if (fmIsGroupKeyFunc(pCtx[j].functionId) || fmisSelectGroupConstValueFunc(pCtx[j].functionId)) {
115,260✔
2588
        bool patched = false;
23,052✔
2589

2590
        if (fmIsGroupKeyFunc(pCtx[j].functionId) && pTaskInfo->pStreamRuntimeInfo != NULL) {
23,052✔
2591
          SStreamRuntimeFuncInfo* pStreamInfo = &pTaskInfo->pStreamRuntimeInfo->funcInfo;
23,052✔
2592
          SArray* pVals = pStreamInfo->pStreamPartColVals;
23,052✔
2593
          if (pStreamInfo->curGrpCalc != NULL && pStreamInfo->curGrpCalc->pGroupColVals != NULL) {
23,052✔
2594
            pVals = pStreamInfo->curGrpCalc->pGroupColVals;
×
2595
          }
2596

2597
          if (pVals != NULL && groupKeyIdx < taosArrayGetSize(pVals)) {
23,052✔
2598
            SStreamGroupValue* pValue = taosArrayGet(pVals, groupKeyIdx);
×
2599
            if (pValue != NULL) {
×
2600
              if (pValue->isNull) {
×
2601
                colDataSetNULL(pColInfo, pBlock->info.rows);
×
2602
              } else if (IS_VAR_DATA_TYPE(pValue->data.type) || pValue->data.type == TSDB_DATA_TYPE_DECIMAL) {
×
2603
                TAOS_CHECK_EXIT(colDataSetVal(pColInfo, pBlock->info.rows, (const char*)pValue->data.pData, false));
×
2604
              } else {
2605
                TAOS_CHECK_EXIT(colDataSetVal(pColInfo, pBlock->info.rows, (const char*)&pValue->data.val, false));
×
2606
              }
2607
              patched = true;
×
2608
            }
2609
          }
2610

2611
          ++groupKeyIdx;
23,052✔
2612
        }
2613

2614
        if (!patched && pAnyRow == NULL) {
23,052✔
2615
          if (!pCCtx->anyRowCached) {
23,052✔
2616
            int32_t numWins = taosArrayGetSize(pCCtx->pWins);
8,136✔
2617
            for (int32_t w = 0; w < numWins; ++w) {
17,176✔
2618
              SExtWinTimeWindow* pW = TARRAY_GET_ELEM(pCCtx->pWins, w);
17,176✔
2619
              if (pW->resWinIdx >= 0) {
17,176✔
2620
                SResultRow* pR = extWinGetResultRowByIdx(pExtW, pW->resWinIdx, pExtW->aggSup.resultRowSize);
8,136✔
2621
                if (pR != NULL && extWinRowHasSourceData(pR)) {
16,272✔
2622
                  pCCtx->pAnyRow = pR;
8,136✔
2623
                  break;
8,136✔
2624
                }
2625
              }
2626
            }
2627
            pCCtx->anyRowCached = true;
8,136✔
2628
          }
2629
          pAnyRow = pCCtx->pAnyRow;
23,052✔
2630
        }
2631

2632
        if (!patched && pAnyRow != NULL) {
23,052✔
2633
          const void* pGroupKeyData = NULL;
23,052✔
2634
          bool        isNull = true;
23,052✔
2635
          if (resultRowGetGroupKeyResult(pAnyRow, j, rowEntryOffset, &pGroupKeyData, &isNull) && !isNull) {
23,052✔
2636
            TAOS_CHECK_EXIT(colDataSetVal(pColInfo, pBlock->info.rows, pGroupKeyData, false));
23,052✔
2637
          }
2638
        }
2639
      }
2640
    }
2641

2642
    pBlock->info.rows += 1;
80,456✔
2643
    TAOS_CHECK_EXIT(extWinApplyAggPostProjection(pOperator, pExtW, pBlock, startRow, 1));
80,456✔
2644
    if (extWinUsesValueFillMode(pExtW->fillMode)) {
160,912✔
2645
      TAOS_CHECK_EXIT(extWinApplyValueFill(pExtW, pBlock, startRow, 1));
49,268✔
2646
    }
2647
    TAOS_CHECK_EXIT(extWinAppendWinIdx(pTaskInfo, pExtW->pWinRowIdx, pBlock, pCCtx->outWinIdx, 1));
80,456✔
2648
    return code;
80,456✔
2649
  }
2650

2651
  char* pTmpBuf = pExtW->pFillRowBuf;
14,464✔
2652
  if (pTmpBuf == NULL) {
14,464✔
2653
    qError("%s pFillRowBuf is NULL, fillMode:%d", __func__, pExtW->fillMode);
×
2654
    return TSDB_CODE_INVALID_PARA;
×
2655
  }
2656
  memcpy(pTmpBuf, pSrcRow, pExtW->aggSup.resultRowSize);
14,464✔
2657
  SResultRow* pTmpRow = (SResultRow*)pTmpBuf;
14,464✔
2658
  pTmpRow->win = pWin->tw;
14,464✔
2659
  pTmpRow->winIdx = pCCtx->outWinIdx;
14,464✔
2660

2661
  // The source row may not have had doUpdateNumOfRows called yet (e.g. NEXT
2662
  // fill copies from a forward window whose numOfRows hasn't been finalized).
2663
  doUpdateNumOfRows(pCtx, pTmpRow, numOfExprs, rowEntryOffset);
14,464✔
2664

2665
  // Patch window pseudo column results (_wstart/_twstart, _wend/_twend) in the
2666
  // copied row so they reflect the current window, not the source (adjacent) window.
2667
  for (int32_t j = 0; j < numOfExprs; ++j) {
51,528✔
2668
    if (!pCtx[j].isPseudoFunc) continue;
37,064✔
2669
    int32_t funcType = pCtx[j].pExpr->pExpr->_function.functionType;
16,724✔
2670
    if (funcType == FUNCTION_TYPE_WSTART || funcType == FUNCTION_TYPE_TWSTART) {
16,724✔
2671
      SResultRowEntryInfo* pEntryInfo = getResultEntryInfo(pTmpRow, j, rowEntryOffset);
14,464✔
2672
      *(int64_t*)GET_ROWCELL_INTERBUF(pEntryInfo) = pWin->tw.skey;
14,464✔
2673
    } else if (funcType == FUNCTION_TYPE_WEND || funcType == FUNCTION_TYPE_TWEND) {
2,260✔
2674
      SResultRowEntryInfo* pEntryInfo = getResultEntryInfo(pTmpRow, j, rowEntryOffset);
×
2675
      *(int64_t*)GET_ROWCELL_INTERBUF(pEntryInfo) = pWin->tw.ekey;
×
2676
    } else if (funcType == FUNCTION_TYPE_WDURATION || funcType == FUNCTION_TYPE_TWDURATION) {
2,260✔
2677
      SResultRowEntryInfo* pEntryInfo = getResultEntryInfo(pTmpRow, j, rowEntryOffset);
×
2678
      *(int64_t*)GET_ROWCELL_INTERBUF(pEntryInfo) = pWin->tw.ekey - pWin->tw.skey;
×
2679
    } else if (funcType == FUNCTION_TYPE_EXTERNAL_WINDOW_COLUMN && pTaskInfo->pStreamRuntimeInfo != NULL) {
2,260✔
2680
      // w.mark is window metadata — always show the current window's own value,
2681
      // not the adjacent (source) window's value copied by memcpy above.
2682
      SStreamRuntimeFuncInfo* pStreamInfo = &pTaskInfo->pStreamRuntimeInfo->funcInfo;
2,260✔
2683
      SNodeList* pParamList = pCtx[j].pExpr->base.pParamList;
2,260✔
2684
      if (pParamList != NULL && LIST_LENGTH(pParamList) >= 2) {
2,260✔
2685
        SNode* pSecond = nodesListGetNode(pParamList, 1);
2,260✔
2686
        if (pSecond != NULL && nodeType(pSecond) == QUERY_NODE_VALUE) {
2,260✔
2687
          int32_t placeholderNo = ((SValueNode*)pSecond)->placeholderNo;
2,260✔
2688
          SSTriggerCalcParam* pCurParam = taosArrayGet(pStreamInfo->pStreamPesudoFuncVals, pCCtx->outWinIdx);
2,260✔
2689
          if (pCurParam != NULL && pCurParam->pExternalWindowData != NULL &&
2,260✔
2690
              placeholderNo >= 0 && placeholderNo < (int32_t)taosArrayGetSize(pCurParam->pExternalWindowData)) {
2,260✔
2691
            SStreamGroupValue* pVal = taosArrayGet(pCurParam->pExternalWindowData, placeholderNo);
2,260✔
2692
            if (pVal != NULL) {
2,260✔
2693
              SResultRowEntryInfo* pEntryInfo = getResultEntryInfo(pTmpRow, j, rowEntryOffset);
2,260✔
2694
              char* dest = GET_ROWCELL_INTERBUF(pEntryInfo);
2,260✔
2695
              if (pVal->isNull) {
2,260✔
2696
                pEntryInfo->isNullRes = 1;
×
2697
              } else {
2698
                pEntryInfo->isNullRes = 0;
2,260✔
2699
                if (IS_VAR_DATA_TYPE(pVal->data.type)) {
2,260✔
2700
                  memcpy(dest, pVal->data.pData, pVal->data.nData);
×
2701
                } else {
2702
                  memcpy(dest, &pVal->data.val, pExprInfo[j].base.resSchema.bytes);
2,260✔
2703
                }
2704
              }
2705
            }
2706
          }
2707
        }
2708
      }
2709
    }
2710
  }
2711

2712
  if (pBlock->info.rows + pTmpRow->numOfRows > pBlock->info.capacity) {
14,464✔
2713
    TAOS_CHECK_EXIT(blockDataEnsureCapacity(pBlock, pBlock->info.rows + pTmpRow->numOfRows));
452✔
2714
  }
2715

2716
  int32_t startRow = pBlock->info.rows;
14,464✔
2717
  updateTimeWindowInfo(&pExtW->twAggSup.timeWindowData, &pTmpRow->win, 0);
14,464✔
2718
  TAOS_CHECK_EXIT(copyResultrowToDataBlock(pExprInfo, numOfExprs, pTmpRow, pCtx, pBlock, rowEntryOffset, pTaskInfo));
14,464✔
2719

2720
  pBlock->info.rows += pTmpRow->numOfRows;
14,464✔
2721
  TAOS_CHECK_EXIT(extWinApplyAggPostProjection(pOperator, pExtW, pBlock, startRow, pTmpRow->numOfRows));
14,464✔
2722
  if (extWinUsesValueFillMode(pExtW->fillMode)) {
28,928✔
2723
    TAOS_CHECK_EXIT(extWinApplyValueFill(pExtW, pBlock, startRow, pTmpRow->numOfRows));
×
2724
  }
2725

2726
  TAOS_CHECK_EXIT(extWinAppendWinIdx(pTaskInfo, pExtW->pWinRowIdx, pBlock, pCCtx->outWinIdx, pTmpRow->numOfRows));
14,464✔
2727

2728
_exit:
14,464✔
2729
  if (code != TSDB_CODE_SUCCESS) {
14,464✔
2730
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
2731
  }
2732
  return code;
14,464✔
2733
}
2734

2735
int32_t extWinInitResRows(SExternalWindowOperator* pExtW, SExecTaskInfo* pTaskInfo) {
1,667,330✔
2736
  int32_t code = 0, lino = 0;
1,667,330✔
2737
  SExtWinResultRows* pRows = &pExtW->resultRows;
1,667,330✔
2738

2739
  pRows->resRowsSize = EXT_WIN_RES_ROWS_ALLOC_SIZE;
1,667,330✔
2740
  pRows->resRowSize = 4096;
1,667,330✔
2741
  pRows->pResultRows = taosMemoryCalloc(pRows->resRowsSize, POINTER_BYTES);
1,667,330✔
2742
  TSDB_CHECK_NULL(pRows->pResultRows, code, lino, _exit, terrno);
1,667,330✔
2743

2744
_exit:
1,667,330✔
2745

2746
  if (code) {
1,667,330✔
2747
    qError("%s %s failed at line %d since %s", GET_TASKID(pTaskInfo), __func__, lino, tstrerror(code));
×
2748
  }
2749

2750
  return code;
1,667,330✔
2751
}
2752

2753

2754
static int32_t extWinAggSetWinOutputBuf(SOperatorInfo* pOperator, SExtWinTimeWindow* win, SExprSupp* pSupp, 
2,147,483,647✔
2755
                                     SAggSupporter* pAggSup, SExecTaskInfo* pTaskInfo) {
2756
  int32_t code = 0, lino = 0;
2,147,483,647✔
2757
  SResultRow* pResultRow = NULL;
2,147,483,647✔
2758
  SExternalWindowOperator* pExtW = (SExternalWindowOperator*)pOperator->info;
2,147,483,647✔
2759

2760
  if (win->resWinIdx >= 0) {
2,147,483,647✔
2761
    pResultRow = extWinGetResultRowByIdx(pExtW, win->resWinIdx, pAggSup->resultRowSize);
1,160,321,915✔
2762
    TSDB_CHECK_NULL(pResultRow, code, lino, _exit, TSDB_CODE_INVALID_PARA);
1,160,321,915✔
2763
  } else {
2764
    win->resWinIdx = pExtW->resWinIdx++;
2,147,483,647✔
2765
    
2766
    qDebug("set window [%" PRId64 ", %" PRId64 "] outIdx:%d", win->tw.skey, win->tw.ekey, win->resWinIdx);
2,147,483,647✔
2767

2768
    TAOS_CHECK_EXIT(extWinGetResultRow(pTaskInfo, pExtW, win->resWinIdx, pAggSup->resultRowSize, &pResultRow));
2,147,483,647✔
2769
    
2770
    memset(pResultRow, 0, pAggSup->resultRowSize);
2,147,483,647✔
2771

2772
    pResultRow->winIdx = extWinGetCurWinIdx(pOperator);
2,147,483,647✔
2773
    TAOS_SET_POBJ_ALIGNED(&pResultRow->win, &win->tw);
2,147,483,647✔
2774
  }
2775

2776
  // set time window for current result
2777
  TAOS_CHECK_EXIT(setResultRowInitCtx(pResultRow, pSupp->pCtx, pSupp->numOfExprs, pSupp->rowEntryInfoOffset));
2,147,483,647✔
2778

2779
_exit:
2,147,483,647✔
2780
  
2781
  if (code) {
2,147,483,647✔
2782
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
2783
  }
2784

2785
  return code;
2,147,483,647✔
2786
}
2787

2788
static int32_t extWinAggDo(SOperatorInfo* pOperator, int32_t startPos, int32_t forwardRows,
2,147,483,647✔
2789
                                  SSDataBlock* pInputBlock) {
2790
  if (pOperator->pTaskInfo->pStreamRuntimeInfo && forwardRows == 0) {
2,147,483,647✔
2791
    return TSDB_CODE_SUCCESS;
×
2792
  }
2793

2794
  SExprSupp*               pSup = &pOperator->exprSupp;
2,147,483,647✔
2795
  SExternalWindowOperator* pExtW = pOperator->info;
2,147,483,647✔
2796
  return applyAggFunctionOnPartialTuples(pOperator->pTaskInfo, pSup->pCtx, &pExtW->twAggSup.timeWindowData, startPos,
2,147,483,647✔
2797
                                         forwardRows, pInputBlock->info.rows, pSup->numOfExprs);
2,147,483,647✔
2798

2799
}
2800

2801
static bool extWinLastWinClosed(SExternalWindowOperator* pExtW) {
11,214,445✔
2802
  if (pExtW->resWinIdx <= 0 || (pExtW->multiTableMode && !pExtW->inputHasOrder)) {
11,214,445✔
2803
    return false;
4,505,625✔
2804
  }
2805

2806
  if (pExtW->pTGrpCtx == NULL || pExtW->pTGrpCtx->pCCtx == NULL || pExtW->pTGrpCtx->pCCtx->lastWinIdx < 0) {
6,708,820✔
2807
    return false;
2,670✔
2808
  }
2809

2810
  if (NULL == pExtW->timeRangeExpr || !pExtW->timeRangeExpr->needCalc) {
6,706,150✔
2811
    return true;
6,706,150✔
2812
  }
2813

2814
  SList* pList = taosArrayGetP(pExtW->pOutputBlocks, pExtW->pTGrpCtx->pCCtx->lastWinIdx);
×
2815
  if (pList == NULL) {
×
2816
    return false;
×
2817
  }
2818
  if (0 == listNEles(pList)) {
×
2819
    return true;
×
2820
  }
2821

2822
  SListNode* pNode = listTail(pList);
×
2823
  SArray* pBlkWinIdx = *((SArray**)pNode->data + 1);
×
2824
  int64_t* pIdx = taosArrayGetLast(pBlkWinIdx);
×
2825
  if (pIdx && *(int32_t*)pIdx < pExtW->pTGrpCtx->pCCtx->blkWinStartIdx) {
×
2826
    return true;
×
2827
  }
2828

2829
  return false;
×
2830
}
2831

2832
static SList** extWinReserveGetBlockList(SExternalWindowOperator* pExtW, SArray* pOutputBlocks, int32_t winIdx) {
4,508,295✔
2833
  SList** ppList = NULL;
4,508,295✔
2834
  if (taosArrayGetSize(pOutputBlocks) > winIdx) {
4,508,295✔
2835
    ppList = taosArrayGet(pOutputBlocks, winIdx);
×
2836
    extWinRecycleBlockList(pExtW, ppList);
×
2837
  } else {
2838
    ppList = taosArrayReserve(pOutputBlocks, 1);
4,508,295✔
2839
  }
2840

2841
  return ppList;
4,508,295✔
2842
}
2843

2844
static int32_t extWinEnsureBlockList(SExternalWindowOperator* pExtW, int32_t winIdx, SList** ppList) {
6,708,820✔
2845
  int32_t code = TSDB_CODE_SUCCESS, lino = 0;
6,708,820✔
2846

2847
  *ppList = taosArrayGetP(pExtW->pOutputBlocks, winIdx);
6,708,820✔
2848
  if (*ppList != NULL) {
6,708,820✔
2849
    return code;
6,708,820✔
2850
  }
2851

2852
  SList** ppSlot = extWinReserveGetBlockList(pExtW, pExtW->pOutputBlocks, winIdx);
×
2853
  TSDB_CHECK_NULL(ppSlot, code, lino, _exit, terrno);
×
2854

2855
  *ppList = tdListNew(POINTER_BYTES * 2);
×
2856
  TSDB_CHECK_NULL(*ppList, code, lino, _exit, terrno);
×
2857

2858
  *ppSlot = *ppList;
×
2859

2860
_exit:
×
2861
  return code;
×
2862
}
2863

2864
static int32_t extWinGetWinResBlock(SOperatorInfo* pOperator, int32_t rows, SExtWinTimeWindow* pWin, SSDataBlock** ppRes, SArray** ppIdx) {
11,217,115✔
2865
  SExternalWindowOperator* pExtW = pOperator->info;
11,217,115✔
2866
  SList*                   pList = NULL;
11,217,115✔
2867
  int32_t                  code = TSDB_CODE_SUCCESS, lino = 0;
11,217,115✔
2868
  
2869
  if (pWin->resWinIdx >= 0) {
11,217,115✔
2870
    TAOS_CHECK_EXIT(extWinEnsureBlockList(pExtW, pWin->resWinIdx, &pList));
2,670✔
2871
  } else {
2872
    if (extWinLastWinClosed(pExtW)) {
11,214,445✔
2873
      pWin->resWinIdx = pExtW->pTGrpCtx->pCCtx->lastWinIdx;
6,706,150✔
2874
      TAOS_CHECK_EXIT(extWinEnsureBlockList(pExtW, pWin->resWinIdx, &pList));
6,706,150✔
2875
    } else {
2876
      pWin->resWinIdx = pExtW->resWinIdx++;
4,508,295✔
2877
      pList = tdListNew(POINTER_BYTES * 2);
4,508,295✔
2878
      TSDB_CHECK_NULL(pList, code, lino, _exit, terrno);
4,508,295✔
2879
      SList** ppList = extWinReserveGetBlockList(pExtW, pExtW->pOutputBlocks, pWin->resWinIdx);
4,508,295✔
2880
      TSDB_CHECK_NULL(ppList, code, lino, _exit, terrno);
4,508,295✔
2881
      *ppList = pList;
4,508,295✔
2882
    }
2883
  }
2884

2885
  pExtW->pTGrpCtx->pCCtx->lastWinIdx = pWin->resWinIdx;
11,217,115✔
2886
  
2887
  TAOS_CHECK_EXIT(extWinGetLastBlockFromList(pOperator, pExtW, pList, rows, ppRes, ppIdx));
11,217,115✔
2888

2889
_exit:
11,216,225✔
2890

2891
  if (code) {
11,216,225✔
2892
    qError("%s %s failed at line %d since %s", pOperator->pTaskInfo->id.str, __func__, lino, tstrerror(code));
×
2893
  }
2894

2895
  return code;
11,216,225✔
2896
}
2897

2898
static int32_t extWinProjectDo(SOperatorInfo* pOperator, SSDataBlock* pInputBlock, int32_t startPos, int32_t rows, SExtWinTimeWindow* pWin) {
11,217,115✔
2899
  SExternalWindowOperator* pExtW = pOperator->info;
11,217,115✔
2900
  SExprSupp*               pExprSup = &pExtW->scalarSupp;
11,217,115✔
2901
  SSDataBlock*             pResBlock = NULL;
11,217,115✔
2902
  SArray*                  pIdx = NULL;
11,217,115✔
2903
  int32_t                  code = TSDB_CODE_SUCCESS, lino = 0;
11,217,115✔
2904
  
2905
  TAOS_CHECK_EXIT(extWinGetWinResBlock(pOperator, rows, pWin, &pResBlock, &pIdx));
11,217,115✔
2906

2907
  qDebug("%s %s win[%" PRId64 ", %" PRId64 "] got res block %p winRowIdx %p, winOutIdx:%d, capacity:%d", 
11,215,780✔
2908
      pOperator->pTaskInfo->id.str, __func__, pWin->tw.skey, pWin->tw.ekey, pResBlock, pIdx, pWin->resWinIdx, pResBlock->info.capacity);
2909
  
2910
  if (!pExtW->pTmpBlock) {
11,216,670✔
2911
    TAOS_CHECK_EXIT(createOneDataBlock(pInputBlock, false, &pExtW->pTmpBlock));
30,705✔
2912
  } else {
2913
    blockDataCleanup(pExtW->pTmpBlock);
11,186,410✔
2914
  }
2915
  
2916
  TAOS_CHECK_EXIT(blockDataEnsureCapacity(pExtW->pTmpBlock, TMAX(1, rows)));
11,217,115✔
2917

2918
  qDebug("%s %s start to copy %d rows to tmp blk", pOperator->pTaskInfo->id.str, __func__, rows);
11,217,115✔
2919
  TAOS_CHECK_EXIT(blockDataMergeNRows(pExtW->pTmpBlock, pInputBlock, startPos, rows));
11,217,115✔
2920

2921
  qDebug("%s %s start to apply project to tmp blk", pOperator->pTaskInfo->id.str, __func__);
11,217,115✔
2922
  TAOS_CHECK_EXIT(projectApplyFunctionsWithSelect(pExprSup->pExprInfo, pResBlock, pExtW->pTmpBlock, pExprSup->pCtx,
11,217,115✔
2923
                                                  pExprSup->numOfExprs, NULL, GET_STM_RTINFO(pOperator->pTaskInfo),
2924
                                                  true, pExprSup->hasIndefRowsFunc, pOperator->pTaskInfo));
2925

2926
  // propagate both ids so downstream (e.g., non-agg output/filters) can align with trigger group
2927
  pResBlock->info.id.groupId = pInputBlock->info.id.groupId;
11,187,745✔
2928
  pResBlock->info.id.baseGId = pInputBlock->info.id.baseGId;
11,215,335✔
2929

2930
  TAOS_CHECK_EXIT(extWinAppendWinIdx(pOperator->pTaskInfo, pIdx, pResBlock, extWinGetCurWinIdx(pOperator), rows));
11,214,890✔
2931

2932
_exit:
11,194,420✔
2933

2934
  if (code) {
11,178,400✔
2935
    qError("%s %s failed at line %d since %s", pOperator->pTaskInfo->id.str, __func__, lino, tstrerror(code));
×
2936
  } else {
2937
    qDebug("%s %s project succeed", pOperator->pTaskInfo->id.str, __func__);
11,178,400✔
2938
  }
2939
  
2940
  return code;
11,193,085✔
2941
}
2942

2943
static int32_t extWinProjectOpen(SOperatorInfo* pOperator, SSDataBlock* pInputBlock) {
40,050✔
2944
  SExternalWindowOperator* pExtW = pOperator->info;
40,050✔
2945
  int64_t*                 tsCol = extWinExtractTsCol(pInputBlock, pExtW->primaryTsIndex, pOperator->pTaskInfo);
40,050✔
2946
  SExtWinTimeWindow*       pWin = NULL;
40,050✔
2947
  bool                     ascScan = pExtW->binfo.inputTsOrder == TSDB_ORDER_ASC;
40,050✔
2948
  int32_t                  startPos = 0, winRows = 0;
40,050✔
2949
  int32_t                  code = TSDB_CODE_SUCCESS, lino = 0;
40,050✔
2950
  
2951
  while (true) {
2952
    TAOS_CHECK_EXIT((*pExtW->getWinFp)(pOperator, tsCol, &startPos, &pInputBlock->info, &pWin, &winRows));
11,256,720✔
2953
    if (pWin == NULL) {
11,257,165✔
2954
      break;
40,050✔
2955
    }
2956

2957
    qDebug("%s ext window [%" PRId64 ", %" PRId64 ") tgrp %" PRId64 " project start, ascScan:%d, startPos:%d, winRows:%d",
11,217,115✔
2958
           GET_TASKID(pOperator->pTaskInfo), pWin->tw.skey, pWin->tw.ekey, pExtW->lastTGrpId, ascScan, startPos, winRows);        
2959
    
2960
    TAOS_CHECK_EXIT(extWinProjectDo(pOperator, pInputBlock, startPos, winRows, pWin));
11,217,115✔
2961
    
2962
    startPos += winRows;
11,216,670✔
2963
  }
2964
  
2965
_exit:
40,050✔
2966

2967
  if (code) {
40,050✔
2968
    qError("%s failed at line %d since:%s", __func__, lino, tstrerror(code));
×
2969
  }
2970

2971
  return code;
40,050✔
2972
}
2973

2974
static int32_t extWinIndefRowsDoImpl(SOperatorInfo* pOperator, SSDataBlock* pRes, SSDataBlock* pBlock) {
×
2975
  SExternalWindowOperator* pExtW = pOperator->info;
×
2976
  SOptrBasicInfo*     pInfo = &pExtW->binfo;
×
2977
  SExprSupp*          pSup = &pOperator->exprSupp;
×
2978
  SExecTaskInfo*      pTaskInfo = pOperator->pTaskInfo;
×
2979
  int32_t order = pInfo->inputTsOrder;
×
2980
  int32_t scanFlag = pBlock->info.scanFlag;
×
2981
  int32_t code = TSDB_CODE_SUCCESS, lino = 0;
×
2982

2983
  SExprSupp* pScalarSup = &pExtW->scalarSupp;
×
2984
  if (pScalarSup->pExprInfo != NULL) {
×
2985
    TAOS_CHECK_EXIT(projectApplyFunctions(pScalarSup->pExprInfo, pBlock, pBlock, pScalarSup->pCtx, pScalarSup->numOfExprs,
×
2986
                                 pExtW->pPseudoColInfo, GET_STM_RTINFO(pOperator->pTaskInfo), pOperator->pTaskInfo));
2987
  }
2988

2989
  TAOS_CHECK_EXIT(setInputDataBlock(pSup, pBlock, order, scanFlag, false));
×
2990

2991
  TAOS_CHECK_EXIT(blockDataEnsureCapacity(pRes, pRes->info.rows + pBlock->info.rows));
×
2992

2993
  TAOS_CHECK_EXIT(projectApplyFunctions(pSup->pExprInfo, pRes, pBlock, pSup->pCtx, pSup->numOfExprs,
×
2994
                               pExtW->pPseudoColInfo, GET_STM_RTINFO(pOperator->pTaskInfo), pOperator->pTaskInfo));
2995

2996
_exit:
×
2997

2998
  if (code) {
×
2999
    qError("%s %s failed at line %d since %s", pOperator->pTaskInfo->id.str, __FUNCTION__, lino, tstrerror(code));
×
3000
  }
3001

3002
  return code;
×
3003
}
3004

3005
static int32_t extWinIndefRowsSetWinOutputBuf(SExternalWindowOperator* pExtW, SExtWinTimeWindow* win, SExprSupp* pSupp, 
×
3006
                                     SAggSupporter* pAggSup, SExecTaskInfo* pTaskInfo, bool reset) {
3007
  int32_t code = 0, lino = 0;
×
3008
  SResultRow* pResultRow = NULL;
×
3009

3010
  TAOS_CHECK_EXIT(extWinGetResultRow(pTaskInfo, pExtW, win->resWinIdx, pAggSup->resultRowSize, &pResultRow));
×
3011
  
3012
  qDebug("set indefRows tgrp %" PRIu64 " window [%" PRId64 ", %" PRId64 "] outIdx:%d", pExtW->lastTGrpId, win->tw.skey, win->tw.ekey, win->resWinIdx);
×
3013

3014
  if (reset) {
×
3015
    memset(pResultRow, 0, pAggSup->resultRowSize);
×
3016
    for (int32_t k = 0; k < pSupp->numOfExprs; ++k) {
×
3017
      SqlFunctionCtx* pCtx = &pSupp->pCtx[k];
×
3018
      pCtx->pOutput = NULL;
×
3019
    }
3020
  }
3021

3022
  TAOS_SET_POBJ_ALIGNED(&pResultRow->win, &win->tw);
×
3023

3024
  // set time window for current result
3025
  TAOS_CHECK_EXIT(setResultRowInitCtx(pResultRow, pSupp->pCtx, pSupp->numOfExprs, pSupp->rowEntryInfoOffset));
×
3026

3027
_exit:
×
3028
  
3029
  if (code) {
×
3030
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
3031
  }
3032

3033
  return code;
×
3034
}
3035

3036
static int32_t extWinGetSetWinResBlockBuf(SOperatorInfo* pOperator, int32_t rows, SExtWinTimeWindow* pWin, SSDataBlock** ppRes, SArray** ppIdx) {
×
3037
  SExternalWindowOperator* pExtW = pOperator->info;
×
3038
  SList*                   pList = NULL;
×
3039
  int32_t                  code = TSDB_CODE_SUCCESS, lino = 0;
×
3040
  
3041
  if (pWin->resWinIdx >= 0) {
×
3042
    TAOS_CHECK_EXIT(extWinEnsureBlockList(pExtW, pWin->resWinIdx, &pList));
×
3043
    TAOS_CHECK_EXIT(extWinIndefRowsSetWinOutputBuf(pExtW, pWin, &pOperator->exprSupp, &pExtW->aggSup, pOperator->pTaskInfo, false));
×
3044
  } else {
3045
    if (extWinLastWinClosed(pExtW)) {
×
3046
      pWin->resWinIdx = pExtW->pTGrpCtx->pCCtx->lastWinIdx;
×
3047
      TAOS_CHECK_EXIT(extWinEnsureBlockList(pExtW, pWin->resWinIdx, &pList));
×
3048
    } else {
3049
      pWin->resWinIdx = pExtW->resWinIdx++;
×
3050
      pList = tdListNew(POINTER_BYTES * 2);
×
3051
      TSDB_CHECK_NULL(pList, code, lino, _exit, terrno);
×
3052
      SList** ppList = extWinReserveGetBlockList(pExtW, pExtW->pOutputBlocks, pWin->resWinIdx);
×
3053
      TSDB_CHECK_NULL(ppList, code, lino, _exit, terrno);
×
3054
      *ppList = pList;
×
3055
    }
3056
    TAOS_CHECK_EXIT(extWinIndefRowsSetWinOutputBuf(pExtW, pWin, &pOperator->exprSupp, &pExtW->aggSup, pOperator->pTaskInfo, true));
×
3057
  }
3058

3059
  pExtW->pTGrpCtx->pCCtx->lastWinIdx = pWin->resWinIdx;
×
3060
  
3061
  TAOS_CHECK_EXIT(extWinGetLastBlockFromList(pOperator, pExtW, pList, rows, ppRes, ppIdx));
×
3062

3063
_exit:
×
3064

3065
  if (code) {
×
3066
    qError("%s %s failed at line %d since %s", pOperator->pTaskInfo->id.str, __func__, lino, tstrerror(code));
×
3067
  }
3068

3069
  return code;
×
3070
}
3071

3072

3073
static int32_t extWinIndefRowsDo(SOperatorInfo* pOperator, SSDataBlock* pInputBlock, int32_t startPos, int32_t rows, SExtWinTimeWindow* pWin) {
×
3074
  SExternalWindowOperator* pExtW = pOperator->info;
×
3075
  SSDataBlock*             pResBlock = NULL;
×
3076
  SArray*                  pIdx = NULL;
×
3077
  int32_t                  code = TSDB_CODE_SUCCESS, lino = 0;
×
3078
  
3079
  TAOS_CHECK_EXIT(extWinGetSetWinResBlockBuf(pOperator, rows, pWin, &pResBlock, &pIdx));
×
3080
  
3081
  if (!pExtW->pTmpBlock) {
×
3082
    TAOS_CHECK_EXIT(createOneDataBlock(pInputBlock, false, &pExtW->pTmpBlock));
×
3083
  } else {
3084
    blockDataCleanup(pExtW->pTmpBlock);
×
3085
  }
3086
  
3087
  TAOS_CHECK_EXIT(blockDataEnsureCapacity(pExtW->pTmpBlock, TMAX(1, rows)));
×
3088

3089
  TAOS_CHECK_EXIT(blockDataMergeNRows(pExtW->pTmpBlock, pInputBlock, startPos, rows));
×
3090
  TAOS_CHECK_EXIT(extWinIndefRowsDoImpl(pOperator, pResBlock, pExtW->pTmpBlock));
×
3091

3092
  pResBlock->info.id.groupId = pInputBlock->info.id.groupId;
×
3093

3094
  TAOS_CHECK_EXIT(extWinAppendWinIdx(pOperator->pTaskInfo, pIdx, pResBlock, extWinGetCurWinIdx(pOperator), rows));
×
3095

3096
_exit:
×
3097

3098
  if (code) {
×
3099
    qError("%s %s failed at line %d since %s", pOperator->pTaskInfo->id.str, __func__, lino, tstrerror(code));
×
3100
  }
3101
  
3102
  return code;
×
3103
}
3104

3105

3106
static int32_t extWinIndefRowsOpen(SOperatorInfo* pOperator, SSDataBlock* pInputBlock) {
×
3107
  SExternalWindowOperator* pExtW = pOperator->info;
×
3108
  int64_t*                 tsCol = extWinExtractTsCol(pInputBlock, pExtW->primaryTsIndex, pOperator->pTaskInfo);
×
3109
  SExtWinTimeWindow*       pWin = NULL;
×
3110
  bool                     ascScan = pExtW->binfo.inputTsOrder == TSDB_ORDER_ASC;
×
3111
  int32_t                  startPos = 0, winRows = 0;
×
3112
  int32_t                  code = TSDB_CODE_SUCCESS, lino = 0;
×
3113
  
3114
  while (true) {
3115
    TAOS_CHECK_EXIT((*pExtW->getWinFp)(pOperator, tsCol, &startPos, &pInputBlock->info, &pWin, &winRows));
×
3116
    if (pWin == NULL) {
×
3117
      break;
×
3118
    }
3119

3120
    qDebug("%s ext window [%" PRId64 ", %" PRId64 ") tgrp %" PRId64 " indefRows start, ascScan:%d, startPos:%d, winRows:%d",
×
3121
           GET_TASKID(pOperator->pTaskInfo), pWin->tw.skey, pWin->tw.ekey, pExtW->lastTGrpId, ascScan, startPos, winRows);        
3122
    
3123
    TAOS_CHECK_EXIT(extWinIndefRowsDo(pOperator, pInputBlock, startPos, winRows, pWin));
×
3124
    
3125
    startPos += winRows;
×
3126
  }
3127
  
3128
_exit:
×
3129

3130
  if (code) {
×
3131
    qError("%s failed at line %d since:%s", __func__, lino, tstrerror(code));
×
3132
  }
3133

3134
  return code;
×
3135
}
3136

3137

3138
static int32_t extWinNonAggOutputSingleGrpRes(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW, SSDataBlock** ppRes) {
4,470,915✔
3139
  SExtWinCalcGrpCtx*  pCCtx = pExtW->pTGrpCtx->pCCtx;
4,470,915✔
3140
  int32_t         numOfWin = taosArrayGetSize(pCCtx->pWins);
4,470,915✔
3141
  int32_t         code = TSDB_CODE_SUCCESS;
4,470,915✔
3142
  int32_t         lino = 0;
4,470,915✔
3143
  SSDataBlock*    pRes = NULL;
4,470,915✔
3144

3145
  for (; pCCtx->outWinIdx < numOfWin && pCCtx->outWinLastIdx < pCCtx->lastWinIdx; pCCtx->outWinIdx += 1, extWinIncCurWinOutIdx(pOperator)) {
4,470,915✔
3146
    SExtWinTimeWindow* pWin = TARRAY_GET_ELEM(pCCtx->pWins, pCCtx->outWinIdx);
4,462,905✔
3147
    if (pWin->resWinIdx < 0 || pWin->resWinIdx == pCCtx->outWinLastIdx) {
4,462,460✔
3148
      continue;
×
3149
    }
3150

3151
    pCCtx->outWinLastIdx = pWin->resWinIdx;
4,462,905✔
3152
    SList* pList = taosArrayGetP(pExtW->pOutputBlocks, pWin->resWinIdx);
4,462,905✔
3153
    if (listNEles(pList) <= 0) {
4,462,460✔
3154
      continue;
×
3155
    }
3156

3157
    SListNode* pNode = tdListPopHead(pList);
4,462,460✔
3158
    pRes = *(SSDataBlock**)pNode->data;
4,462,905✔
3159
    pOperator->pTaskInfo->pStreamRuntimeInfo->funcInfo.pStreamBlkWinIdx = *(SArray**)((SArray**)pNode->data + 1);
4,462,905✔
3160
    pExtW->pLastBlkNode = pNode;
4,462,905✔
3161

3162
    if (listNEles(pList) <= 0) {
4,462,905✔
3163
      pCCtx->outWinIdx++;
4,462,905✔
3164
      extWinIncCurWinOutIdx(pOperator);
4,462,905✔
3165
    }
3166

3167
    break;
4,462,460✔
3168
  }
3169

3170
_exit:
8,455✔
3171

3172
  *ppRes = pRes;
4,470,915✔
3173

3174
  if (code != TSDB_CODE_SUCCESS) {
4,470,470✔
3175
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
3176
  }
3177
  
3178
  return code;
4,470,470✔
3179
}
3180

3181
static int32_t extWinNonAggOutputMultiGrpRes(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW, SSDataBlock** ppRes) {
4,484,265✔
3182
  int32_t         code = TSDB_CODE_SUCCESS;
4,484,265✔
3183
  int32_t         lino = 0;
4,484,265✔
3184
  SStreamRuntimeFuncInfo* pStream = &pOperator->pTaskInfo->pStreamRuntimeInfo->funcInfo;
4,484,265✔
3185
  SSDataBlock*    pBlock = NULL;
4,483,375✔
3186

3187
  if (pStream->curGrpCalc) {
4,483,820✔
3188
    TAOS_CHECK_EXIT(extWinNonAggOutputSingleGrpRes(pOperator, pExtW, &pBlock));
4,462,460✔
3189
  }
3190
  if (pBlock == NULL || pBlock->info.rows == 0) {
4,483,375✔
3191
    pStream->curGrpCalc = tSimpleHashIterate(pStream->pGroupCalcInfos, pStream->curGrpCalc, &pExtW->lastOutputIter);
29,815✔
3192
    while (pStream->curGrpCalc != NULL) {
55,180✔
3193
      if (pStream->curGrpCalc->pRunnerGrpCtx) {
33,820✔
3194
        pExtW->lastTGrpId = *(uint64_t*)tSimpleHashGetKey(pStream->curGrpCalc, NULL);
16,910✔
3195
        pExtW->pTGrpCtx = pStream->curGrpCalc->pRunnerGrpCtx;
8,455✔
3196
        pExtW->ownTGrpCtx = false;
8,455✔
3197
        
3198
        TAOS_CHECK_EXIT(extWinNonAggOutputSingleGrpRes(pOperator, pExtW, &pBlock));
8,455✔
3199
        if (pBlock != NULL && pBlock->info.rows > 0) {
8,455✔
3200
          break;
8,455✔
3201
        }
3202
      }
3203
      
3204
      pStream->curGrpCalc = tSimpleHashIterate(pStream->pGroupCalcInfos, pStream->curGrpCalc, &pExtW->lastOutputIter);
25,365✔
3205
    }
3206
  }
3207

3208
  extWinPostUpdateStreamRt(pStream, pOperator, pExtW);
4,483,375✔
3209

3210
_exit:
4,484,265✔
3211

3212
  *ppRes = pBlock;
4,484,265✔
3213

3214
  if (code != TSDB_CODE_SUCCESS) {
4,484,265✔
3215
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
3216
  }
3217

3218
  return code;
4,484,265✔
3219
}
3220

3221

3222
static int32_t extWinNonAggOutputRes(SOperatorInfo* pOperator, SSDataBlock** ppRes) {
4,567,035✔
3223
  SExternalWindowOperator* pExtW = pOperator->info;
4,567,035✔
3224
  int32_t                  numOfWin = pExtW->resWinIdx;
4,567,035✔
3225
  int32_t                  code = TSDB_CODE_SUCCESS;
4,566,590✔
3226
  int32_t                  lino = 0;
4,566,590✔
3227
  SSDataBlock*             pRes = NULL;
4,566,590✔
3228
  SStreamRuntimeFuncInfo*  pStream = &pOperator->pTaskInfo->pStreamRuntimeInfo->funcInfo;
4,566,590✔
3229

3230
  if (pStream->isMultiGroupCalc) {
4,567,035✔
3231
    TAOS_CHECK_EXIT(extWinNonAggOutputMultiGrpRes(pOperator, pExtW, &pRes));
4,484,265✔
3232
  } else {
3233
    for (; pExtW->outWinIdx < numOfWin; pExtW->outWinIdx++, extWinIncCurWinOutIdx(pOperator)) {
82,770✔
3234
      SList* pList = taosArrayGetP(pExtW->pOutputBlocks, pExtW->outWinIdx);
45,835✔
3235
      if (listNEles(pList) <= 0) {
45,835✔
3236
        continue;
×
3237
      }
3238

3239
      SListNode* pNode = tdListPopHead(pList);
45,835✔
3240
      pRes = *(SSDataBlock**)pNode->data;
45,835✔
3241
      pStream->pStreamBlkWinIdx = *(SArray**)((SArray**)pNode->data + 1);
45,835✔
3242
      pExtW->pLastBlkNode = pNode;
45,835✔
3243

3244
      if (listNEles(pList) <= 0) {
45,835✔
3245
        pExtW->outWinIdx++;
44,500✔
3246
        extWinIncCurWinOutIdx(pOperator);
44,500✔
3247
      }
3248

3249
      break;
45,835✔
3250
    }
3251
  }
3252

3253
  if (pRes) {
4,567,035✔
3254
    qDebug("%s result generated, rows:%" PRId64 , GET_TASKID(pOperator->pTaskInfo), pRes->info.rows);
4,508,740✔
3255
    pRes->info.version = pOperator->pTaskInfo->version;
4,508,740✔
3256
    pRes->info.dataLoad = 1;
4,508,740✔
3257
  } else {
3258
    pStream->pStreamBlkWinIdx = NULL;
58,295✔
3259
    qDebug("%s ext window done", GET_TASKID(pOperator->pTaskInfo));
58,295✔
3260
  }
3261

3262
  *ppRes = (pRes && pRes->info.rows > 0) ? pRes : NULL;
4,567,035✔
3263

3264
_exit:
4,567,035✔
3265

3266
  if (code != TSDB_CODE_SUCCESS) {
4,567,035✔
3267
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
3268
  }
3269

3270
  return code;
4,567,035✔
3271
}
3272

3273
static int32_t extWinAggHandleEmptyWins(SOperatorInfo* pOperator, SSDataBlock* pBlock, bool allRemains, SExtWinTimeWindow* pWin) {
2,147,483,647✔
3274
  int32_t code = 0, lino = 0;
2,147,483,647✔
3275
  SExternalWindowOperator* pExtW = (SExternalWindowOperator*)pOperator->info;
2,147,483,647✔
3276
  SExprSupp* pSup = &pOperator->exprSupp;
2,147,483,647✔
3277
  int32_t currIdx = extWinGetCurWinIdx(pOperator);
2,147,483,647✔
3278

3279
  if (NULL == pExtW->pEmptyInputBlock || NULL == pExtW->pTGrpCtx || NULL == pExtW->pTGrpCtx->pCCtx ||
2,147,483,647✔
3280
      NULL == pExtW->pTGrpCtx->pCCtx->pWins) {
141,095✔
3281
    goto _exit;
2,147,483,647✔
3282
  }
3283

3284
  // When fill mode is active, empty windows are handled during output emission via extWinAppendAggFilledRow.
3285
  // Running aggregation on empty input blocks here is unnecessary work.
3286
  if (pExtW->fillMode != FILL_MODE_NONE) {
141,540✔
3287
    goto _exit;
141,540✔
3288
  }
3289

3290
  if (pWin && pWin->tw.skey == pExtW->pTGrpCtx->pCCtx->lastSKey &&
×
3291
      pWin->tw.ekey == pExtW->pTGrpCtx->pCCtx->lastEKey) {
×
3292
    goto _exit;
×
3293
  }
3294

3295
  bool ascScan = pExtW->binfo.inputTsOrder == TSDB_ORDER_ASC;
×
3296
  int32_t endIdx = allRemains ? (pExtW->pTGrpCtx->pCCtx->pWins->size - 1) : (currIdx - 1);
×
3297
  SResultRowInfo* pResultRowInfo = &pExtW->binfo.resultRowInfo;
×
3298
  SSDataBlock* pInput = pExtW->pEmptyInputBlock;
×
3299

3300
  if ((pExtW->pTGrpCtx->pCCtx->lastWinId + 1) <= endIdx) {
×
3301
    TAOS_CHECK_EXIT(setInputDataBlock(pSup, pExtW->pEmptyInputBlock, pExtW->binfo.inputTsOrder, MAIN_SCAN, true));
×
3302
  }
3303
  
3304
  for (int32_t i = pExtW->pTGrpCtx->pCCtx->lastWinId + 1; i <= endIdx; ++i) {
×
3305
    SExtWinTimeWindow* pWin = taosArrayGet(pExtW->pTGrpCtx->pCCtx->pWins, i);
×
3306

3307
    extWinSetCurWinIdx(pOperator, i);
×
3308
    qDebug("%s tgrp %" PRIu64 " cgrp %" PRIu64 " %dth ext empty window start:%" PRId64 ", end:%" PRId64 ", ascScan:%d",
×
3309
           GET_TASKID(pOperator->pTaskInfo), pExtW->lastTGrpId, pExtW->lastCGrpId, i, pWin->tw.skey, pWin->tw.ekey, ascScan);
3310

3311
    TAOS_CHECK_EXIT(extWinAggSetWinOutputBuf(pOperator, pWin, pSup, &pExtW->aggSup, pOperator->pTaskInfo));
×
3312

3313
    updateTimeWindowInfo(&pExtW->twAggSup.timeWindowData, &pWin->tw, 1);
×
3314
    code = extWinAggDo(pOperator, 0, 1, pInput);
×
3315
    pExtW->pTGrpCtx->pCCtx->lastWinId = i;  
×
3316
    TAOS_CHECK_EXIT(code);
×
3317
  }
3318

3319
  
3320
_exit:
×
3321

3322
  if (code) {
2,147,483,647✔
3323
    qError("%s %s failed at line %d since %s", pOperator->pTaskInfo->id.str, __FUNCTION__, lino, tstrerror(code));
×
3324
  } else {
3325
    if (pBlock) {
2,147,483,647✔
3326
      TAOS_CHECK_EXIT(setInputDataBlock(pSup, pBlock, pExtW->binfo.inputTsOrder, pBlock->info.scanFlag, true));
2,147,483,647✔
3327
    }
3328

3329
    if (!allRemains) {
2,147,483,647✔
3330
      extWinSetCurWinIdx(pOperator, currIdx);  
2,147,483,647✔
3331
    }
3332
  }
3333

3334
  return code;
2,147,483,647✔
3335
}
3336

3337
static int32_t extWinAggOpen(SOperatorInfo* pOperator, SSDataBlock* pInputBlock) {
4,056,190✔
3338
  SExternalWindowOperator* pExtW = (SExternalWindowOperator*)pOperator->info;
4,056,190✔
3339
  int32_t                  startPos = 0, winRows = 0;
4,056,190✔
3340
  int64_t*                 tsCol = extWinExtractTsCol(pInputBlock, pExtW->primaryTsIndex, pOperator->pTaskInfo);
4,056,190✔
3341
  bool                     ascScan = pExtW->binfo.inputTsOrder == TSDB_ORDER_ASC;
4,056,190✔
3342
  int32_t                  code = 0, lino = 0;
4,056,190✔
3343
  SExtWinTimeWindow*       pWin = NULL;
4,056,190✔
3344
  bool                     scalarCalc = false;
4,056,190✔
3345

3346
  while (true) {
2,147,483,647✔
3347
    TAOS_CHECK_EXIT((*pExtW->getWinFp)(pOperator, tsCol, &startPos, &pInputBlock->info, &pWin, &winRows));
2,147,483,647✔
3348
    if (pWin == NULL) {
2,147,483,647✔
3349
      break;
4,056,190✔
3350
    }
3351

3352
    TAOS_CHECK_EXIT(extWinAggHandleEmptyWins(pOperator, pInputBlock, false, pWin));
2,147,483,647✔
3353

3354
    qDebug("%s ext window [%" PRId64 ", %" PRId64 ") tgrp %" PRIu64 " cgrp %" PRIu64 " agg start, ascScan:%d, startPos:%d, winRows:%d",
2,147,483,647✔
3355
           GET_TASKID(pOperator->pTaskInfo), pWin->tw.skey, pWin->tw.ekey, pExtW->lastTGrpId, pExtW->lastCGrpId, ascScan, startPos, winRows);        
3356

3357
    if (!scalarCalc) {
2,147,483,647✔
3358
      if (pExtW->scalarSupp.pExprInfo) {
3,919,747✔
3359
        SExprSupp* pScalarSup = &pExtW->scalarSupp;
3,560✔
3360
        TAOS_CHECK_EXIT(projectApplyFunctions(pScalarSup->pExprInfo, pInputBlock, pInputBlock, pScalarSup->pCtx, pScalarSup->numOfExprs,
3,560✔
3361
                                     pExtW->pPseudoColInfo, GET_STM_RTINFO(pOperator->pTaskInfo), pOperator->pTaskInfo));
3362
      }
3363

3364
      scalarCalc = true;
3,919,747✔
3365
    }
3366

3367
    if (pWin->tw.skey != pExtW->pTGrpCtx->pCCtx->lastSKey || pWin->tw.ekey != pExtW->pTGrpCtx->pCCtx->lastEKey ||
2,147,483,647✔
3368
        pWin->tw.skey == INT64_MIN) {
640,139✔
3369
      TAOS_CHECK_EXIT(
2,147,483,647✔
3370
          extWinAggSetWinOutputBuf(pOperator, pWin, &pOperator->exprSupp, &pExtW->aggSup, pOperator->pTaskInfo));
3371
    }
3372

3373
    updateTimeWindowInfo(&pExtW->twAggSup.timeWindowData, &pWin->tw, 1);
2,147,483,647✔
3374
    TAOS_CHECK_EXIT(extWinAggDo(pOperator, startPos, winRows, pInputBlock));
2,147,483,647✔
3375

3376
    SResultRow* pCurRow = extWinGetResultRowByIdx(pExtW, pWin->resWinIdx, pExtW->aggSup.resultRowSize);
2,147,483,647✔
3377
    if (pCurRow) {
2,147,483,647✔
3378
      pCurRow->nOrigRows += winRows;
2,147,483,647✔
3379
    }
3380
    
3381
    pExtW->pTGrpCtx->pCCtx->lastSKey = pWin->tw.skey;
2,147,483,647✔
3382
    pExtW->pTGrpCtx->pCCtx->lastEKey = pWin->tw.ekey;
2,147,483,647✔
3383
    pExtW->pTGrpCtx->pCCtx->lastWinId = extWinGetCurWinIdx(pOperator);
2,147,483,647✔
3384
    startPos += winRows;
2,147,483,647✔
3385
  }
3386

3387
_exit:
4,056,190✔
3388

3389
  if (code) {
4,056,190✔
3390
    qError("%s failed at line %d since:%s", __func__, lino, tstrerror(code));
×
3391
  }
3392

3393
  return code;
4,056,190✔
3394
}
3395

3396
static int32_t extWinAggOutputSingleCGrpRes(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW, bool* grpDone) {
1,927,734✔
3397
  int32_t            code = TSDB_CODE_SUCCESS;
1,927,734✔
3398
  int32_t            lino = 0;
1,927,734✔
3399
  SExprInfo*         pExprInfo = pOperator->exprSupp.pExprInfo;
1,927,734✔
3400
  int32_t            numOfExprs = pOperator->exprSupp.numOfExprs;
1,927,734✔
3401
  int32_t*           rowEntryOffset = pOperator->exprSupp.rowEntryInfoOffset;
1,927,734✔
3402
  SqlFunctionCtx*    pCtx = pOperator->exprSupp.pCtx;
1,927,734✔
3403
  SSDataBlock*       pBlock = pExtW->binfo.pRes;
1,927,734✔
3404
  SExecTaskInfo*     pTaskInfo = pOperator->pTaskInfo;
1,927,734✔
3405
  SExtWinTrigGrpCtx* pTGrpCtx = pExtW->pTGrpCtx;
1,927,734✔
3406
  if (!pExtW->pTGrpCtx) {
1,927,734✔
3407
    return TSDB_CODE_SUCCESS;
118,815✔
3408
  }
3409
  SExtWinCalcGrpCtx* pCCtx = pTGrpCtx->pCCtx;
1,808,919✔
3410
  int32_t            numOfWin = taosArrayGetSize(pCCtx->pWins);
1,808,919✔
3411

3412
  if (pExtW->fillMode == FILL_MODE_NONE) {
1,808,919✔
3413
    for (; pCCtx->outWinIdx < numOfWin && (pExtW->isDynWindow || pCCtx->outWinNum < pCCtx->outWinTotalNum);
2,147,483,647✔
3414
         pCCtx->outWinIdx += 1) {
2,147,483,647✔
3415
      SExtWinTimeWindow* pWin = TARRAY_GET_ELEM(pCCtx->pWins, pCCtx->outWinIdx);
2,147,483,647✔
3416
      bool               emptyWin = false;
2,147,483,647✔
3417

3418
      if (pWin->resWinIdx < 0) {
2,147,483,647✔
3419
        emptyWin = true;
348,573,999✔
3420
      } else {
3421
        SResultRow* pRow = extWinGetResultRowByIdx(pExtW, pWin->resWinIdx, pExtW->aggSup.resultRowSize);
2,147,483,647✔
3422
        doUpdateNumOfRows(pCtx, pRow, numOfExprs, rowEntryOffset);
2,147,483,647✔
3423
        if (pRow->numOfRows == 0) {
2,147,483,647✔
3424
          emptyWin = true;
×
3425
        }
3426
      }
3427

3428
      if (emptyWin) {
2,147,483,647✔
3429
        if (!pExtW->isDynWindow) {
348,573,999✔
3430
          continue;
142,887✔
3431
        }
3432
        if (pBlock->info.rows + 1 > pBlock->info.capacity) {
348,431,112✔
3433
          uint32_t newSize = pBlock->info.rows + 1 + numOfWin - pCCtx->outWinIdx;
×
3434
          TAOS_CHECK_EXIT(blockDataEnsureCapacity(pBlock, newSize));
×
3435
        }
3436
        for (int32_t j = 0; j < taosArrayGetSize(pBlock->pDataBlock); ++j) {
903,738,484✔
3437
          SColumnInfoData* pColInfo = taosArrayGet(pBlock->pDataBlock, j);
555,307,372✔
3438
          if (pColInfo) {
555,307,372✔
3439
            colDataSetNULL(pColInfo, pBlock->info.rows);
555,307,372✔
3440
          }
3441
        }
3442
        pBlock->info.rows += 1;
348,431,112✔
3443
        pCCtx->outWinNum++;
348,431,112✔
3444
        TAOS_CHECK_EXIT(extWinAppendWinIdx(pOperator->pTaskInfo, pExtW->pWinRowIdx, pBlock, pCCtx->outWinIdx, 1));
348,431,112✔
3445

3446
        if (pBlock->info.rows >= pOperator->resultInfo.threshold) {
348,431,112✔
3447
          ++pCCtx->outWinIdx;
72,711✔
3448
          break;
72,711✔
3449
        }
3450
        continue;
348,358,401✔
3451
      }
3452

3453
      pCCtx->outWinNum++;
2,147,483,647✔
3454
      SResultRow* pRow = extWinGetResultRowByIdx(pExtW, pWin->resWinIdx, pExtW->aggSup.resultRowSize);
2,147,483,647✔
3455
      if (pBlock->info.rows + pRow->numOfRows > pBlock->info.capacity) {
2,147,483,647✔
3456
        uint32_t newSize =
1,343,281✔
3457
            pBlock->info.rows + pRow->numOfRows + (pExtW->isDynWindow ? numOfWin - pCCtx->outWinIdx
1,343,281✔
3458
                                                                       : pCCtx->outWinTotalNum - pCCtx->outWinNum);
178,918✔
3459
        TAOS_CHECK_EXIT(blockDataEnsureCapacity(pBlock, newSize));
1,343,281✔
3460
        qDebug("datablock capacity not sufficient, expand to required:%d, current capacity:%d, %s", newSize,
1,343,281✔
3461
               pBlock->info.capacity, GET_TASKID(pTaskInfo));
3462
      }
3463

3464
      updateTimeWindowInfo(&pExtW->twAggSup.timeWindowData, &pRow->win, 0);
2,147,483,647✔
3465
      TAOS_CHECK_EXIT(copyResultrowToDataBlock(pExprInfo, numOfExprs, pRow, pCtx, pBlock, rowEntryOffset, pTaskInfo));
2,147,483,647✔
3466

3467
      int32_t startRow = pBlock->info.rows;
2,147,483,647✔
3468
      pBlock->info.rows += pRow->numOfRows;
2,147,483,647✔
3469
      TAOS_CHECK_EXIT(extWinApplyAggPostProjection(pOperator, pExtW, pBlock, startRow, pRow->numOfRows));
2,147,483,647✔
3470
      TAOS_CHECK_EXIT(extWinAppendWinIdx(pOperator->pTaskInfo, pExtW->pWinRowIdx, pBlock, pRow->winIdx, pRow->numOfRows));
2,147,483,647✔
3471

3472
      if (pBlock->info.rows >= pOperator->resultInfo.threshold) {
2,147,483,647✔
3473
        ++pCCtx->outWinIdx;
1,083,484✔
3474
        break;
1,083,484✔
3475
      }
3476
    }
3477

3478
    if (grpDone) {
1,716,637✔
3479
      *grpDone = pExtW->isDynWindow ? (pCCtx->outWinIdx >= numOfWin)
34,710✔
3480
                                    : (pCCtx->outWinIdx >= numOfWin || pCCtx->outWinNum >= pCCtx->outWinTotalNum);
17,355✔
3481
    }
3482

3483
    return code;
1,716,637✔
3484
  }
3485

3486
  bool               emitAllWins = extWinShouldEmitAllWindows(pExtW, pCCtx);
92,282✔
3487

3488
  // For vtable COLS merge (isDynWindow), iterate all windows including empty ones to output NULL placeholder rows;
3489
  // for normal queries, stop at outWinTotalNum (windows with actual results).
3490
  for (; pCCtx->outWinIdx < numOfWin && (emitAllWins || pCCtx->outWinNum < pCCtx->outWinTotalNum); pCCtx->outWinIdx += 1) {
278,850✔
3491
    SExtWinTimeWindow* pWin = TARRAY_GET_ELEM(pCCtx->pWins, pCCtx->outWinIdx);
186,568✔
3492
    bool emptyWin = false;
186,568✔
3493
    SResultRow* pRow = NULL;
186,568✔
3494

3495
    if (pWin->resWinIdx < 0) {
186,568✔
3496
      emptyWin = true;
94,920✔
3497
    } else {
3498
      pRow = extWinGetResultRowByIdx(pExtW, pWin->resWinIdx, pExtW->aggSup.resultRowSize);
91,648✔
3499
      doUpdateNumOfRows(pCtx, pRow, numOfExprs, rowEntryOffset);
91,648✔
3500
      if (!extWinRowHasSourceData(pRow)) {
91,648✔
3501
        emptyWin = true;
×
3502
      }
3503
    }
3504

3505
    if (emptyWin) {
186,568✔
3506
      if (!emitAllWins) {
94,920✔
3507
        continue;
×
3508
      }
3509

3510
      SResultRow* pFillRow = NULL;
94,920✔
3511
      if (pExtW->fillMode == FILL_MODE_PREV) {
94,920✔
3512
        pFillRow = extWinFindAdjacentFillRow(pExtW, pCCtx, pCCtx->outWinIdx, false);
17,628✔
3513
      } else if (pExtW->fillMode == FILL_MODE_NEXT) {
77,292✔
3514
        pFillRow = extWinFindAdjacentFillRow(pExtW, pCCtx, pCCtx->outWinIdx, true);
7,232✔
3515
      }
3516
      TAOS_CHECK_EXIT(extWinAppendAggFilledRow(pOperator, pExtW, pCCtx, pWin, pFillRow));
94,920✔
3517
      pCCtx->outWinNum++;
94,920✔
3518

3519
      if (pBlock->info.rows >= pOperator->resultInfo.threshold) {
94,920✔
3520
        ++pCCtx->outWinIdx;
×
3521
        break;
×
3522
      }
3523
      continue;
94,920✔
3524
    }
3525

3526
    pCCtx->outWinNum++;
91,648✔
3527

3528
    if (pBlock->info.rows + pRow->numOfRows > pBlock->info.capacity) {
91,648✔
3529
      uint32_t newSize = pBlock->info.rows + pRow->numOfRows + ((emitAllWins || pExtW->isDynWindow) ? (numOfWin - pCCtx->outWinIdx) : (pCCtx->outWinTotalNum - pCCtx->outWinNum));
34,212✔
3530
      TAOS_CHECK_EXIT(blockDataEnsureCapacity(pBlock, newSize));
34,212✔
3531
      qDebug("datablock capacity not sufficient, expand to required:%d, current capacity:%d, %s", newSize,
34,212✔
3532
             pBlock->info.capacity, GET_TASKID(pTaskInfo));
3533
    }
3534

3535
    updateTimeWindowInfo(&pExtW->twAggSup.timeWindowData, &pRow->win, 0);
91,648✔
3536
    TAOS_CHECK_EXIT(copyResultrowToDataBlock(pExprInfo, numOfExprs, pRow, pCtx, pBlock, rowEntryOffset, pTaskInfo));
91,648✔
3537

3538
    int32_t startRow = pBlock->info.rows;
91,648✔
3539
    pBlock->info.rows += pRow->numOfRows;
91,648✔
3540
    TAOS_CHECK_EXIT(extWinApplyAggPostProjection(pOperator, pExtW, pBlock, startRow, pRow->numOfRows));
91,648✔
3541

3542
    TAOS_CHECK_EXIT(extWinAppendWinIdx(pOperator->pTaskInfo, pExtW->pWinRowIdx, pBlock, pRow->winIdx, pRow->numOfRows));
91,648✔
3543

3544
    if (pBlock->info.rows >= pOperator->resultInfo.threshold) {
91,648✔
3545
      ++pCCtx->outWinIdx;
×
3546
      break;
×
3547
    }
3548
  }
3549

3550
  if (grpDone) {
92,282✔
3551
    // isDynWindow: done when all windows (including empty) are emitted; otherwise done when result count is met
3552
    *grpDone = (emitAllWins || pExtW->isDynWindow) ? (pCCtx->outWinIdx >= numOfWin)
1,780✔
3553
                                                   : (pCCtx->outWinIdx >= numOfWin || pCCtx->outWinNum >= pCCtx->outWinTotalNum);
1,780✔
3554
  }
3555

3556
_exit:
91,392✔
3557

3558
  if (code != TSDB_CODE_SUCCESS) {
92,282✔
3559
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
3560
  }
3561
  
3562
  return code;
92,282✔
3563
}
3564

3565
static int32_t extWinAggOutputMulNoOrderCGrpsRes(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW) {
221,035✔
3566
  int32_t code = 0, lino = 0;
221,035✔
3567
  SExtWinTrigGrpCtx* pTGrpCtx = pExtW->pTGrpCtx;
221,035✔
3568
  SSDataBlock*    pBlock = pExtW->binfo.pRes;
221,035✔
3569

3570
  if (pTGrpCtx == NULL) {
221,035✔
3571
    return TSDB_CODE_SUCCESS;
45,390✔
3572
  }
3573

3574
  if (pTGrpCtx->pCCtx) {
175,645✔
3575
    pExtW->lastCGrpId = pTGrpCtx->pCCtx->groupId;
101,642✔
3576
    TAOS_CHECK_EXIT(extWinAggOutputSingleCGrpRes(pOperator, pExtW, NULL));
101,642✔
3577
  }
3578
  if (0 == pBlock->info.rows) {
175,645✔
3579
    pTGrpCtx->pCCtx = tSimpleHashIterate(pTGrpCtx->pCGCtxs, pTGrpCtx->pCCtx, &pTGrpCtx->lastCtxIter);
174,755✔
3580
    while (pTGrpCtx->pCCtx != NULL) {
286,895✔
3581
      pExtW->lastCGrpId = pTGrpCtx->pCCtx->groupId;
212,892✔
3582
      TAOS_CHECK_EXIT(extWinAggOutputSingleCGrpRes(pOperator, pExtW, NULL));
212,892✔
3583
      if (pBlock->info.rows > 0) {
212,892✔
3584
        break;
100,752✔
3585
      }
3586
      
3587
      pTGrpCtx->pCCtx = tSimpleHashIterate(pTGrpCtx->pCGCtxs, pTGrpCtx->pCCtx, &pTGrpCtx->lastCtxIter);
112,140✔
3588
    }
3589
  }
3590

3591
_exit:
74,893✔
3592

3593
  if (code != TSDB_CODE_SUCCESS) {
175,645✔
3594
    qError("%s %s failed at line %d since %s", GET_TASKID(pOperator->pTaskInfo), __func__, lino, tstrerror(code));
×
3595
  }
3596

3597
  return code;
175,645✔
3598
}
3599

3600

3601

3602
static int32_t extWinAggOutputMulNoOrderTGrpsRes(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW) {
66,750✔
3603
  int32_t         code = TSDB_CODE_SUCCESS;
66,750✔
3604
  int32_t         lino = 0;
66,750✔
3605
  SStreamRuntimeFuncInfo* pStream = &pOperator->pTaskInfo->pStreamRuntimeInfo->funcInfo;
66,750✔
3606
  SSDataBlock*    pBlock = pExtW->binfo.pRes;
66,750✔
3607

3608
  if (pStream->curGrpCalc) {
66,750✔
3609
    pExtW->lastTGrpId = *(uint64_t*)tSimpleHashGetKey(pStream->curGrpCalc, NULL);
39,160✔
3610
    pExtW->pTGrpCtx = pStream->curGrpCalc->pRunnerGrpCtx;
19,580✔
3611
    pExtW->ownTGrpCtx = false;
19,580✔
3612
    if (pExtW->calcWithPartition) {
19,580✔
3613
      TAOS_CHECK_EXIT(extWinAggOutputMulNoOrderCGrpsRes(pOperator, pExtW));
19,580✔
3614
    } else {
3615
      TAOS_CHECK_EXIT(extWinAggOutputSingleCGrpRes(pOperator, pExtW, NULL));
×
3616
    }
3617
  }
3618
  if (0 == pBlock->info.rows) {
66,750✔
3619
    pStream->curGrpCalc = tSimpleHashIterate(pStream->pGroupCalcInfos, pStream->curGrpCalc, &pExtW->lastOutputIter);
65,860✔
3620
    while (pStream->curGrpCalc != NULL) {
151,300✔
3621
      if (pStream->curGrpCalc->pRunnerGrpCtx) {
104,130✔
3622
        pExtW->lastTGrpId = *(uint64_t*)tSimpleHashGetKey(pStream->curGrpCalc, NULL);
37,380✔
3623
        pExtW->pTGrpCtx = pStream->curGrpCalc->pRunnerGrpCtx;
18,690✔
3624
        pExtW->ownTGrpCtx = false;
18,690✔
3625
        pExtW->lastCGrpId = 0;
18,690✔
3626

3627
        if (pExtW->calcWithPartition) {
18,690✔
3628
          // Same ownership rule as in extWinPrepareForOutput (see comment there).
3629
          if (pExtW->pTGrpCtx->pCCtx && pExtW->pTGrpCtx->pCGCtxs == NULL) {
18,690✔
3630
            extWinDestroyCGrpCtx(pExtW->pTGrpCtx->pCCtx);
×
3631
            taosMemoryFree(pExtW->pTGrpCtx->pCCtx);
×
3632
          }
3633
          pExtW->pTGrpCtx->pCCtx = NULL;
18,690✔
3634
          TAOS_CHECK_EXIT(extWinAggOutputMulNoOrderCGrpsRes(pOperator, pExtW));
18,690✔
3635
        } else {
3636
          TAOS_CHECK_EXIT(extWinAggOutputSingleCGrpRes(pOperator, pExtW, NULL));
×
3637
        }
3638
        
3639
        if (pBlock->info.rows > 0) {
18,690✔
3640
          break;
18,690✔
3641
        }
3642
      }
3643
      
3644
      pStream->curGrpCalc = tSimpleHashIterate(pStream->pGroupCalcInfos, pStream->curGrpCalc, &pExtW->lastOutputIter);
85,440✔
3645
    }
3646
  }
3647

3648
  extWinPostUpdateStreamRt(pStream, pOperator, pExtW);
66,750✔
3649

3650
_exit:
66,750✔
3651

3652
  if (code != TSDB_CODE_SUCCESS) {
66,750✔
3653
    qError("%s %s failed at line %d since %s", GET_TASKID(pOperator->pTaskInfo), __func__, lino, tstrerror(code));
×
3654
  }
3655

3656
  return code;
66,750✔
3657
}
3658

3659

3660
static int32_t extWinAggOutputMulOrderCGrpsRes(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW) {
45,390✔
3661
  int32_t code = 0, lino = 0;
45,390✔
3662
  SExtWinTrigGrpCtx* pTGrpCtx = pExtW->pTGrpCtx;
45,390✔
3663
  SSDataBlock*    pBlock = pExtW->binfo.pRes;
45,390✔
3664
  int32_t grpNum = taosArrayGetSize(pExtW->pGrpIds);
45,390✔
3665
  bool grpDone = false;
45,390✔
3666

3667
  if (pTGrpCtx == NULL || pTGrpCtx->pCGCtxs == NULL) {
45,390✔
3668
    return TSDB_CODE_SUCCESS;
15,575✔
3669
  }
3670

3671
  for (; pExtW->lastGrpIdx < grpNum; ++pExtW->lastGrpIdx) {
29,815✔
3672
    uint64_t *grpId = taosArrayGet(pExtW->pGrpIds, pExtW->lastGrpIdx);
15,130✔
3673
    pTGrpCtx->pCCtx = tSimpleHashGet(pTGrpCtx->pCGCtxs, grpId, sizeof(*grpId));
15,130✔
3674
    TSDB_CHECK_NULL(pTGrpCtx->pCCtx, code, lino, _exit, terrno);
15,130✔
3675
    TAOS_CHECK_EXIT(extWinAggOutputSingleCGrpRes(pOperator, pExtW, &grpDone));
15,130✔
3676
    if (pBlock->info.rows > 0) {
15,130✔
3677
      if (grpDone) {
15,130✔
3678
        pExtW->lastGrpIdx++;
15,130✔
3679
      }
3680
      
3681
      break;
15,130✔
3682
    }
3683
  }
3684

3685
_exit:
29,815✔
3686

3687
  if (code != TSDB_CODE_SUCCESS) {
29,815✔
3688
    qError("%s %s failed at line %d since %s", GET_TASKID(pOperator->pTaskInfo), __func__, lino, tstrerror(code));
×
3689
  }
3690

3691
  return code;
29,815✔
3692
}
3693

3694
static int32_t extWinAggOutputMulOrderTGrpsRes(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW) {
×
3695
  int32_t code = 0, lino = 0;
×
3696
  SExtWinTrigGrpCtx* pTGrpCtx = pExtW->pTGrpCtx;
×
3697
  SSDataBlock*    pBlock = pExtW->binfo.pRes;
×
3698
  int32_t grpNum = taosArrayGetSize(pExtW->pGrpIds);
×
3699
  SStreamRuntimeFuncInfo* pStream = &pOperator->pTaskInfo->pStreamRuntimeInfo->funcInfo;
×
3700
  bool grpDone = false;
×
3701

3702
  for (; pExtW->lastGrpIdx < grpNum; ++pExtW->lastGrpIdx) {
×
3703
    uint64_t *grpId = taosArrayGet(pExtW->pGrpIds, pExtW->lastGrpIdx);
×
3704

3705
    pStream->curGrpCalc = tSimpleHashGet(pStream->pGroupCalcInfos, grpId, sizeof(*grpId));
×
3706
    TSDB_CHECK_NULL(pStream->curGrpCalc, code, lino, _exit, terrno);
×
3707
    pExtW->pTGrpCtx = pStream->curGrpCalc->pRunnerGrpCtx;
×
3708
    pExtW->ownTGrpCtx = false;
×
3709

3710
    TAOS_CHECK_EXIT(extWinAggOutputSingleCGrpRes(pOperator, pExtW, &grpDone));
×
3711
    if (pBlock->info.rows > 0) {
×
3712
      if (grpDone) {
×
3713
        pExtW->lastGrpIdx++;
×
3714
      }
3715

3716
      break;
×
3717
    }
3718
  }
3719

3720
  extWinPostUpdateStreamRt(pStream, pOperator, pExtW);
×
3721

3722
_exit:
×
3723

3724
  if (code != TSDB_CODE_SUCCESS) {
×
3725
    qError("%s %s failed at line %d since %s", GET_TASKID(pOperator->pTaskInfo), __func__, lino, tstrerror(code));
×
3726
  }
3727

3728
  return code;
×
3729
}
3730

3731
static int32_t extWinAggOutputMulOrderTCGrpsRes(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW) {
10,235✔
3732
  int32_t code = 0, lino = 0;
10,235✔
3733
  SExtWinTrigGrpCtx* pTGrpCtx = pExtW->pTGrpCtx;
10,235✔
3734
  SSDataBlock*    pBlock = pExtW->binfo.pRes;
10,235✔
3735
  int32_t grpNum = taosArrayGetSize(pExtW->pCTGrpIds);
10,235✔
3736
  SStreamRuntimeFuncInfo* pStream = &pOperator->pTaskInfo->pStreamRuntimeInfo->funcInfo;
10,235✔
3737
  bool grpDone = false;
10,235✔
3738

3739
  for (; pExtW->lastGrpIdx < grpNum; ++pExtW->lastGrpIdx) {
10,235✔
3740
    uint64_t* cGrpId = taosArrayGet(pExtW->pCTGrpIds, pExtW->lastGrpIdx);
3,115✔
3741
    uint64_t* tGrpId = cGrpId + 1;
3,115✔
3742

3743
    pStream->curGrpCalc = tSimpleHashGet(pStream->pGroupCalcInfos, tGrpId, sizeof(*tGrpId));
3,115✔
3744
    TSDB_CHECK_NULL(pStream->curGrpCalc, code, lino, _exit, terrno);
3,115✔
3745
    pExtW->pTGrpCtx = pStream->curGrpCalc->pRunnerGrpCtx;
3,115✔
3746
    pExtW->ownTGrpCtx = false;
3,115✔
3747

3748
    pExtW->pTGrpCtx->pCCtx = tSimpleHashGet(pExtW->pTGrpCtx->pCGCtxs, cGrpId, sizeof(*cGrpId));
3,115✔
3749
    TSDB_CHECK_NULL(pExtW->pTGrpCtx->pCCtx, code, lino, _exit, terrno);
3,115✔
3750

3751
    TAOS_CHECK_EXIT(extWinAggOutputSingleCGrpRes(pOperator, pExtW, &grpDone));
3,115✔
3752
    if (pBlock->info.rows > 0) {
3,115✔
3753
      if (grpDone) {
3,115✔
3754
        pExtW->lastGrpIdx++;
3,115✔
3755
      }
3756
      
3757
      break;
3,115✔
3758
    }
3759
  }
3760

3761
  extWinPostUpdateStreamRt(pStream, pOperator, pExtW);
10,235✔
3762
  
3763
_exit:
10,235✔
3764

3765
  if (code != TSDB_CODE_SUCCESS) {
10,235✔
3766
    qError("%s %s failed at line %d since %s", GET_TASKID(pOperator->pTaskInfo), __func__, lino, tstrerror(code));
×
3767
  }
3768

3769
  return code;
10,235✔
3770
}
3771

3772

3773
static int32_t extWinAggOutputRes(SOperatorInfo* pOperator, SSDataBlock** ppRes) {
1,900,095✔
3774
  SExternalWindowOperator* pExtW = pOperator->info;
1,900,095✔
3775
  SExecTaskInfo*  pTaskInfo = pOperator->pTaskInfo;
1,900,095✔
3776
  SSDataBlock*    pBlock = pExtW->binfo.pRes;
1,900,095✔
3777
  int32_t         code = TSDB_CODE_SUCCESS;
1,900,095✔
3778
  int32_t         lino = 0;
1,900,095✔
3779
  SStreamRuntimeFuncInfo*  pStream = pTaskInfo->pStreamRuntimeInfo ? &pTaskInfo->pStreamRuntimeInfo->funcInfo : NULL;
1,900,095✔
3780

3781
  pBlock->info.version = pTaskInfo->version;
1,900,095✔
3782
  blockDataCleanup(pBlock);
1,900,095✔
3783
  taosArrayClear(pExtW->pWinRowIdx);
1,900,095✔
3784

3785
  if (pExtW->needGroupSort) {
1,900,095✔
3786
    if (pStream && pStream->isMultiGroupCalc) {
55,625✔
3787
      if (pExtW->calcWithPartition) {
10,235✔
3788
        TAOS_CHECK_EXIT(extWinAggOutputMulOrderTCGrpsRes(pOperator, pExtW));
10,235✔
3789
      } else {
3790
        TAOS_CHECK_EXIT(extWinAggOutputMulOrderTGrpsRes(pOperator, pExtW));
×
3791
      }
3792
    } else {
3793
      TAOS_CHECK_EXIT(extWinAggOutputMulOrderCGrpsRes(pOperator, pExtW));
45,390✔
3794
    }
3795
  } else {
3796
    if (pStream && pStream->isMultiGroupCalc) {
1,844,470✔
3797
      TAOS_CHECK_EXIT(extWinAggOutputMulNoOrderTGrpsRes(pOperator, pExtW));
66,750✔
3798
    } else if (pExtW->calcWithPartition) {
1,777,720✔
3799
      TAOS_CHECK_EXIT(extWinAggOutputMulNoOrderCGrpsRes(pOperator, pExtW));
182,765✔
3800
    } else {
3801
      TAOS_CHECK_EXIT(extWinAggOutputSingleCGrpRes(pOperator, pExtW, NULL));
1,594,955✔
3802
    }
3803
  }
3804
  
3805
  qDebug("%s result generated, rows:%" PRId64 ", groupId:%" PRIu64, GET_TASKID(pTaskInfo), pBlock->info.rows,
1,900,095✔
3806
         pBlock->info.id.groupId);
3807

3808
  int32_t rowsBeforeFilter = pBlock->info.rows;
1,900,095✔
3809
  SColumnInfoData* pFilterRes = NULL;
1,900,095✔
3810
  SColMatchInfo* pMatchInfo = pExtW->fillMatchInfo.pList != NULL ? &pExtW->fillMatchInfo : NULL;
1,900,095✔
3811
  TAOS_CHECK_EXIT(doFilter(pBlock, pOperator->exprSupp.pFilterInfo, pMatchInfo, &pFilterRes));
1,900,095✔
3812
  if (pBlock->info.rows < rowsBeforeFilter) {
1,900,095✔
3813
    if (pFilterRes != NULL) {
8,546✔
3814
      TAOS_CHECK_EXIT(extWinRebuildWinIdxByFilter(pTaskInfo, pExtW->pWinRowIdx, rowsBeforeFilter, pFilterRes));
8,546✔
3815
    } else {
3816
      // no indicator means all rows are filtered out by short-circuit path
3817
      taosArrayClear(pExtW->pWinRowIdx);
×
3818
    }
3819
  }
3820

3821
  pBlock->info.dataLoad = 1;
1,900,095✔
3822
  extWinAssignBlockGrpId(pOperator, pExtW, &pBlock->info.id);
1,900,095✔
3823

3824
  qDebug("%s result generated, rows:%" PRId64 ", cGrpId:%" PRIu64 " baseGid:%" PRIu64, 
1,900,095✔
3825
    GET_TASKID(pTaskInfo), pBlock->info.rows, pBlock->info.id.groupId, pBlock->info.id.baseGId);
3826

3827
  *ppRes = (pBlock->info.rows > 0) ? pBlock : NULL;
1,900,095✔
3828

3829
  if (*ppRes) {
1,900,095✔
3830
    (*ppRes)->info.window.skey = pExtW->orgTableTimeRange.skey;
1,459,027✔
3831
    (*ppRes)->info.window.ekey = pExtW->orgTableTimeRange.ekey;
1,459,027✔
3832
  }
3833
  if (pOperator->pTaskInfo->pStreamRuntimeInfo) {
1,900,095✔
3834
    pOperator->pTaskInfo->pStreamRuntimeInfo->funcInfo.pStreamBlkWinIdx = pExtW->pWinRowIdx;
689,479✔
3835
  }
3836

3837
_exit:
1,900,095✔
3838
  colDataDestroy(pFilterRes);
1,900,095✔
3839
  taosMemoryFree(pFilterRes);
1,900,095✔
3840

3841
  if (code != TSDB_CODE_SUCCESS) {
1,900,095✔
3842
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
3843
  }
3844

3845
  return code;
1,900,095✔
3846
}
3847

3848
static void extWinFreeResultRow(SExternalWindowOperator* pExtW) {
499,363✔
3849
  if (pExtW->resultRows.resRowAllcNum * pExtW->aggSup.resultRowSize >= 1048576) {
499,363✔
3850
    int32_t i = 1;
33,375✔
3851
    while (i < pExtW->resultRows.resRowsSize && pExtW->resultRows.pResultRows[i]) {
33,375✔
3852
      taosMemoryFreeClear(pExtW->resultRows.pResultRows[i]);
×
3853
      pExtW->resultRows.resRowAllcNum -= pExtW->resultRows.resRowSize;
×
3854
      i++;
×
3855
    }
3856
  }
3857
  
3858
  if (pExtW->binfo.pRes && pExtW->binfo.pRes->info.rows * pExtW->aggSup.resultRowSize >= 1048576) {
499,363✔
3859
    blockDataFreeCols(pExtW->binfo.pRes);
×
3860
  }
3861
}
499,363✔
3862

3863
static bool extWinNonAggGotResBlock(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW) {
40,050✔
3864
  if (pExtW->calcWithPartition) {
40,050✔
3865
    return false;
20,470✔
3866
  }
3867

3868
  if ((pExtW->multiTableMode && !pExtW->inputHasOrder) || pExtW->needGroupSort) {
19,580✔
3869
    return false;
13,350✔
3870
  }
3871
  int32_t remainWin = pExtW->resWinIdx - pExtW->outWinIdx;
6,230✔
3872
  if (remainWin > 1 && (NULL == pExtW->timeRangeExpr || !pExtW->timeRangeExpr->needCalc)) {
6,230✔
3873
    return true;
×
3874
  }
3875
  
3876
  SList* pList = taosArrayGetP(pExtW->pOutputBlocks, pExtW->outWinIdx);
6,230✔
3877
  if (!pList || listNEles(pList) <= 0) {
6,230✔
3878
    return false;
×
3879
  }
3880
  if (listNEles(pList) > 1) {
6,230✔
3881
    return true;
445✔
3882
  }
3883

3884
  SListNode* pNode = listHead(pList);
5,785✔
3885
  SArray* pIdx = *(SArray**)((SArray**)pNode->data + 1);
5,785✔
3886
  int32_t* winIdx = taosArrayGetLast(pIdx);
5,785✔
3887
  if (winIdx && *winIdx < pExtW->pTGrpCtx->pCCtx->blkWinStartIdx) {
5,785✔
3888
    return true;
2,225✔
3889
  }
3890

3891
  return false;
3,560✔
3892
}
3893

3894
static int32_t getTimeWindowOfBlock(SSDataBlock *pBlock, col_id_t tsSlotId, int64_t *startTs, int64_t *endTs) {
3,692,580✔
3895
  int32_t code = TSDB_CODE_SUCCESS;
3,692,580✔
3896
  int32_t lino = 0;
3,692,580✔
3897
  int32_t tsIndex = -1;
3,692,580✔
3898
  for (int32_t i = 0; i < taosArrayGetSize(pBlock->pDataBlock); i++) {
5,025,184✔
3899
    SColumnInfoData *pCol = (SColumnInfoData*)taosArrayGet(pBlock->pDataBlock, i);
5,025,184✔
3900
    QUERY_CHECK_NULL(pCol, code, lino, _return, terrno)
5,025,184✔
3901
    if (pCol->info.colId == tsSlotId) {
5,025,184✔
3902
      tsIndex = i;
3,692,580✔
3903
      break;
3,692,580✔
3904
    }
3905
  }
3906

3907
  if (tsIndex == -1) {
3,692,580✔
3908
    tsIndex = (int32_t)taosArrayGetSize(pBlock->pDataBlock) - 1;
×
3909
  }
3910

3911
  SColumnInfoData *pColData = (SColumnInfoData*)taosArrayGet(pBlock->pDataBlock, tsIndex);
3,692,580✔
3912
  QUERY_CHECK_NULL(pColData, code, lino, _return, terrno)
3,692,580✔
3913

3914
  GET_TYPED_DATA(*startTs, int64_t, TSDB_DATA_TYPE_TIMESTAMP, colDataGetNumData(pColData, 0), 0);
3,692,580✔
3915
  GET_TYPED_DATA(*endTs, int64_t, TSDB_DATA_TYPE_TIMESTAMP, colDataGetNumData(pColData, pBlock->info.rows - 1), 0);
3,692,580✔
3916

3917
  return code;
3,692,580✔
3918
_return:
×
3919
  qError("failed to get time window of block, %s code:%s, line:%d", __func__, tstrerror(code), lino);
×
3920
  return code;
×
3921
}
3922

3923
static void extWinEndClearCtxs(SExternalWindowOperator* pExtW, SExecTaskInfo* pTaskInfo) {
1,713,539✔
3924
  if (pTaskInfo == NULL || pTaskInfo->pStreamRuntimeInfo == NULL) {
1,713,539✔
3925
    return;
1,210,616✔
3926
  }
3927

3928
  SStreamRuntimeFuncInfo* pInfo = &pTaskInfo->pStreamRuntimeInfo->funcInfo;
502,923✔
3929
  if (!pInfo->isMultiGroupCalc) {
502,923✔
3930
    return;
427,273✔
3931
  }
3932

3933
  pInfo->pStreamPesudoFuncVals = NULL;
75,650✔
3934
  pInfo->pStreamPartColVals = NULL;
75,650✔
3935
}
3936

3937

3938
static int32_t extWinAggHandleMultiTGrpEmptyWins(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW) {
54,290✔
3939
  int32_t         code = TSDB_CODE_SUCCESS;
54,290✔
3940
  int32_t         lino = 0;
54,290✔
3941
  int32_t         iter = 0;
54,290✔
3942
  SExecTaskInfo*  pTaskInfo = pOperator->pTaskInfo;
54,290✔
3943
  SStreamRuntimeFuncInfo* pStream = &pTaskInfo->pStreamRuntimeInfo->funcInfo;
54,290✔
3944
  SSTriggerGroupCalcInfo* pGroup = NULL;
54,290✔
3945

3946
  if (NULL == pExtW->pEmptyInputBlock) {
54,290✔
3947
    goto _exit;
54,290✔
3948
  }
3949
  
3950
  pGroup = tSimpleHashIterate(pStream->pGroupCalcInfos, NULL, &iter);
×
3951
  while (pGroup != NULL) {
×
3952
    pExtW->lastTGrpId = *(uint64_t*)tSimpleHashGetKey(pGroup, NULL);
×
3953

3954
    if (NULL == pGroup->pRunnerGrpCtx) {
×
3955
      pStream->curGrpCalc = pGroup;
×
3956

3957
      SExtWinTrigGrpCtx* pTGCtx = taosMemoryCalloc(1, sizeof(SExtWinTrigGrpCtx));
×
3958
      TSDB_CHECK_NULL(pTGCtx, code, lino, _exit, terrno);
×
3959

3960
      pTGCtx->pCCtx = taosMemoryCalloc(1, sizeof(*pTGCtx->pCCtx));
×
3961
      TSDB_CHECK_NULL(pTGCtx->pCCtx, code, lino, _exit, terrno);
×
3962
      TAOS_CHECK_EXIT(extWinInitCGrpCtx(pExtW, pOperator->pTaskInfo, pTGCtx->pCCtx));
×
3963
      pGroup->pRunnerGrpCtx = pTGCtx;
×
3964
    }
3965

3966
    pExtW->pTGrpCtx = pGroup->pRunnerGrpCtx;
×
3967
    pExtW->ownTGrpCtx = false;
×
3968
    TAOS_CHECK_EXIT(extWinAggHandleEmptyWins(pOperator, NULL, true, NULL));
×
3969
    
3970
    pGroup = tSimpleHashIterate(pStream->pGroupCalcInfos, pGroup, &iter);
×
3971
  }
3972

3973
_exit:
54,290✔
3974

3975
  if (code != TSDB_CODE_SUCCESS) {
54,290✔
3976
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
3977
  }
3978

3979
  return code;
54,290✔
3980
}
3981

3982
static void extWinPrepareForOutput(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW) {
1,710,869✔
3983
  pExtW->lastGrpIdx = 0;
1,710,869✔
3984
  pExtW->lastOutputIter = 0;
1,710,869✔
3985
  pExtW->lastTGrpId = 0;
1,710,869✔
3986
  pExtW->lastCGrpId = 0;
1,710,869✔
3987

3988
  if (pOperator == NULL || pOperator->pTaskInfo == NULL || pOperator->pTaskInfo->pStreamRuntimeInfo == NULL) {
1,710,869✔
3989
    return;
1,210,616✔
3990
  }
3991

3992
  SStreamRuntimeFuncInfo* pStream = &pOperator->pTaskInfo->pStreamRuntimeInfo->funcInfo;
500,253✔
3993

3994
  pStream->curGrpCalc = NULL;
500,253✔
3995
  pStream->curGrpRead = NULL;
500,253✔
3996

3997
  if (pExtW->needGroupSort) {
500,253✔
3998
    if (pStream->isMultiGroupCalc && pExtW->calcWithPartition) {
37,380✔
3999
      if (pExtW->pCTGrpIds) {
7,120✔
4000
        taosArraySort(pExtW->pCTGrpIds, extWinGrpIdCompare);
3,115✔
4001
      }
4002
    } else {
4003
      if (pExtW->pGrpIds) {
30,260✔
4004
        taosArraySort(pExtW->pGrpIds, extWinGrpIdCompare);
14,685✔
4005
      }
4006
    }
4007
  }
4008

4009
  // In multi-group output mode, always restart from iterator state instead of
4010
  // inheriting the calc phase's current group/cgrp cursor.
4011
  if (pStream->isMultiGroupCalc) {
500,253✔
4012
    pExtW->pTGrpCtx = NULL;
75,650✔
4013
    pExtW->ownTGrpCtx = false;
75,650✔
4014
  }
4015

4016
  if ((!pStream->isMultiGroupCalc) && pExtW->calcWithPartition && pExtW->pTGrpCtx) {
500,253✔
4017
    // pCGCtxs==NULL: pCCtx is a standalone taosMemoryCalloc allocation owned
4018
    //   by pTGrpCtx; free it before clearing or extWinDestroyTGrpCtx will miss it.
4019
    // pCGCtxs!=NULL: pCCtx is a raw pointer into hash-table inline storage;
4020
    //   must not be freed here — tSimpleHashCleanup releases it.
4021
    if (pExtW->pTGrpCtx->pCCtx && pExtW->pTGrpCtx->pCGCtxs == NULL) {
78,453✔
4022
      extWinDestroyCGrpCtx(pExtW->pTGrpCtx->pCCtx);
3,588✔
4023
      taosMemoryFree(pExtW->pTGrpCtx->pCCtx);
3,588✔
4024
    }
4025
    pExtW->pTGrpCtx->pCCtx = NULL;
78,453✔
4026
  }
4027
}
4028

4029
static int32_t extWinEnsureDynParamOpenCtx(SExternalWindowOperator* pExtW) {
1,211,964✔
4030
  int32_t                 code = TSDB_CODE_SUCCESS;
1,211,964✔
4031
  int32_t                 lino = 0;
1,211,964✔
4032
  if (pExtW->pTGrpCtx == NULL) {
1,211,964✔
4033
    pExtW->pTGrpCtx = taosMemoryCalloc(1, sizeof(*pExtW->pTGrpCtx));
1,165,711✔
4034
    TSDB_CHECK_NULL(pExtW->pTGrpCtx, code, lino, _exit, terrno);
1,165,711✔
4035
    pExtW->ownTGrpCtx = true;
1,165,711✔
4036
  }
4037

4038
  if (pExtW->pTGrpCtx->pCCtx == NULL) {
1,211,964✔
4039
    pExtW->pTGrpCtx->pCCtx = taosMemoryCalloc(1, sizeof(*pExtW->pTGrpCtx->pCCtx));
1,165,711✔
4040
    TSDB_CHECK_NULL(pExtW->pTGrpCtx->pCCtx, code, lino, _exit, terrno);
1,165,711✔
4041
    extWinInitDynParamCGrpCtx(pExtW->pTGrpCtx->pCCtx);
1,165,711✔
4042
  }
4043

4044
_exit:
46,253✔
4045
  if (code != TSDB_CODE_SUCCESS) {
1,211,964✔
4046
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
×
4047
  }
4048

4049
  return code;
1,211,964✔
4050
}
4051

4052
static int32_t extWinOpen(SOperatorInfo* pOperator) {
1,714,887✔
4053
  if (OPTR_IS_OPENED(pOperator) && !pOperator->pOperatorGetParam) {
1,714,887✔
4054
    return TSDB_CODE_SUCCESS;
×
4055
  }
4056

4057
  int32_t                  code = 0;
1,714,442✔
4058
  int32_t                  lino = 0;
1,714,442✔
4059
  SExecTaskInfo*           pTaskInfo = pOperator->pTaskInfo;
1,714,442✔
4060
  SOperatorInfo*           pDownstream = pOperator->pDownstream[0];
1,714,887✔
4061
  SExternalWindowOperator* pExtW = pOperator->info;
1,714,887✔
4062
  SExprSupp*               pSup = &pOperator->exprSupp;
1,714,442✔
4063
  SStreamRuntimeFuncInfo*  pInfo = pTaskInfo->pStreamRuntimeInfo ? &pTaskInfo->pStreamRuntimeInfo->funcInfo : NULL;
1,714,442✔
4064

4065
  if (pOperator->pOperatorGetParam) {
1,714,442✔
4066
    SOperatorParam*               pParam = (SOperatorParam*)(pOperator->pOperatorGetParam);
1,211,964✔
4067
    SOperatorParam*               pDownParam = (SOperatorParam*)(pOperator->pDownstreamGetParams[0]);
1,211,964✔
4068
    SExchangeOperatorParam*       pExecParam = NULL;
1,211,964✔
4069
    SExternalWindowOperatorParam* pExtPram = (SExternalWindowOperatorParam*)pParam->value;
1,211,964✔
4070

4071
    TAOS_CHECK_EXIT(extWinEnsureDynParamOpenCtx(pExtW));
1,211,964✔
4072

4073
    if (pExtW->pTGrpCtx->pCCtx->pWins) {
1,211,964✔
4074
      taosArrayDestroy(pExtW->pTGrpCtx->pCCtx->pWins);
46,253✔
4075
    }
4076

4077
    extWinInitDynParamCGrpCtx(pExtW->pTGrpCtx->pCCtx);
1,211,964✔
4078
    pExtW->pTGrpCtx->pCCtx->pWins = pExtPram->ExtWins;
1,211,964✔
4079

4080
    pExtPram->ExtWins = NULL;
1,211,964✔
4081
    pExtW->outWinIdx = 0;
1,211,964✔
4082
    pExtW->isDynWindow = true;
1,211,964✔
4083
    pExtW->orgTableTimeRange.skey = INT64_MAX;
1,211,964✔
4084
    pExtW->orgTableTimeRange.ekey = INT64_MIN;
1,211,964✔
4085

4086
    QUERY_CHECK_CONDITION(pOperator->numOfDownstream == 1, code, lino, _exit, TSDB_CODE_INVALID_PARA)
1,211,964✔
4087

4088
    switch (pDownParam->opType) {
1,211,964✔
4089
      case QUERY_NODE_PHYSICAL_PLAN_EXCHANGE: {
1,211,964✔
4090
        pExecParam = (SExchangeOperatorParam*)((SOperatorParam*)(pOperator->pDownstreamGetParams[0]))->value;
1,211,964✔
4091
        if (!pExecParam->multiParams) {
1,211,964✔
4092
          pExecParam->basic.vgId = pExtW->orgTableVgId;
937,164✔
4093
          taosArrayClear(pExecParam->basic.uidList);
937,164✔
4094
          QUERY_CHECK_NULL(taosArrayPush(pExecParam->basic.uidList, &pExtW->orgTableUid), code, lino, _exit, terrno)
1,874,328✔
4095
        }
4096
        break;
1,211,964✔
4097
      }
4098
      default:
×
4099
        break;
×
4100
    }
4101

4102
    freeOperatorParam(pOperator->pOperatorGetParam, OP_GET_PARAM);
1,211,964✔
4103
    pOperator->pOperatorGetParam = NULL;
1,211,964✔
4104
  }
4105

4106
  while (1) {
4,121,160✔
4107
    SSDataBlock* pBlock = getNextBlockFromDownstreamRemainDetach(pOperator, 0);
5,835,602✔
4108
    if (pBlock == NULL) {
5,834,699✔
4109
      if (EEXT_MODE_AGG == pExtW->mode) {
1,710,869✔
4110
        if (pInfo && pInfo->isMultiGroupCalc) {
1,651,684✔
4111
          TAOS_CHECK_EXIT(extWinAggHandleMultiTGrpEmptyWins(pOperator, pExtW));
54,290✔
4112
        } else {
4113
          if (pExtW->pEmptyInputBlock && pExtW->pTGrpCtx == NULL) {
1,597,394✔
4114
            pExtW->pTGrpCtx = taosMemoryCalloc(1, sizeof(*pExtW->pTGrpCtx));
15,680✔
4115
            TSDB_CHECK_NULL(pExtW->pTGrpCtx, code, lino, _exit, terrno);
15,680✔
4116
            pExtW->ownTGrpCtx = true;
15,680✔
4117
            pExtW->pTGrpCtx->pCCtx = taosMemoryCalloc(1, sizeof(*pExtW->pTGrpCtx->pCCtx));
15,680✔
4118
            TSDB_CHECK_NULL(pExtW->pTGrpCtx->pCCtx, code, lino, _exit, terrno);
15,680✔
4119
            TAOS_CHECK_EXIT(extWinInitCGrpCtx(pExtW, pOperator->pTaskInfo, pExtW->pTGrpCtx->pCCtx));
15,680✔
4120
          }
4121
          TAOS_CHECK_EXIT(extWinAggHandleEmptyWins(pOperator, pBlock, true, NULL));
1,597,394✔
4122
        }
4123
      }
4124

4125
      break;
1,710,869✔
4126
    }
4127

4128
    if (pExtW->isDynWindow) {
4,123,830✔
4129
      TSKEY skey = 0;
3,692,580✔
4130
      TSKEY ekey = 0;
3,692,580✔
4131
      code = getTimeWindowOfBlock(pBlock, pExtW->primaryTsIndex, &skey, &ekey);
3,692,580✔
4132
      QUERY_CHECK_CODE(code, lino, _exit);
3,692,580✔
4133
      pExtW->orgTableTimeRange.skey = TMIN(pExtW->orgTableTimeRange.skey, skey);
3,692,580✔
4134
      pExtW->orgTableTimeRange.ekey = TMAX(pExtW->orgTableTimeRange.ekey, ekey);
3,692,580✔
4135
    }
4136

4137
    printDataBlock(pBlock, __func__, pTaskInfo->id.str, pTaskInfo->id.queryId);
4,123,830✔
4138

4139
    qInfo("%s ext window mode:%s baseGrp:%" PRIu64 " grp:%" PRIu64 " got %" PRId64 " rows from downstream",
4,123,385✔
4140
        GET_TASKID(pTaskInfo), extWinModeStr(pExtW->mode), pBlock->info.id.baseGId, pBlock->info.id.groupId, pBlock->info.rows);
4141

4142
    // Fallback for non-stream grouped external-window:
4143
    // 1) if outer calc is partitioned and downstream only carries groupId,
4144
    //    treat groupId as the corresponding trigger-group id;
4145
    // 2) otherwise, if there is exactly one trigger group from subquery,
4146
    //    use that singleton gid.
4147
    if (pBlock->info.id.baseGId == 0 && pTaskInfo->pStreamRuntimeInfo &&
4,123,830✔
4148
        pTaskInfo->pStreamRuntimeInfo->funcInfo.isMultiGroupCalc &&
416,565✔
4149
        pTaskInfo->pStreamRuntimeInfo->funcInfo.pGroupCalcInfos) {
44,945✔
4150
      if (pExtW->calcWithPartition && pBlock->info.id.groupId != 0 &&
89,890✔
4151
          tSimpleHashGet(pTaskInfo->pStreamRuntimeInfo->funcInfo.pGroupCalcInfos,
44,945✔
4152
                         &pBlock->info.id.groupId, sizeof(pBlock->info.id.groupId)) != NULL) {
44,945✔
4153
        pBlock->info.id.baseGId = pBlock->info.id.groupId;
31,150✔
4154
        qDebug("%s extWin fallback baseGId <- matched groupId %" PRIu64,
31,150✔
4155
               GET_TASKID(pTaskInfo), pBlock->info.id.groupId);
4156
      } else {
4157
        int32_t __iter = 0;
13,795✔
4158
        int32_t __size = tSimpleHashGetSize(pTaskInfo->pStreamRuntimeInfo->funcInfo.pGroupCalcInfos);
13,795✔
4159
        if (__size == 1) {
13,795✔
4160
          SSTriggerGroupCalcInfo* __one = tSimpleHashIterate(pTaskInfo->pStreamRuntimeInfo->funcInfo.pGroupCalcInfos, NULL, &__iter);
5,340✔
4161
          if (__one) {
5,340✔
4162
            uint64_t __gid = *(uint64_t*)tSimpleHashGetKey(__one, NULL);
5,340✔
4163
            pBlock->info.id.baseGId = __gid;
5,340✔
4164
            qDebug("%s extWin fallback baseGId set to single gid %" PRIu64, GET_TASKID(pTaskInfo), __gid);
5,340✔
4165
          }
4166
        }
4167
      }
4168
    }
4169

4170
    // Equality gating in partitioned multi-group mode:
4171
    // process the block only when baseGId == groupId; otherwise skip it.
4172
    if (pTaskInfo->pStreamRuntimeInfo &&
4,123,830✔
4173
        pTaskInfo->pStreamRuntimeInfo->funcInfo.isMultiGroupCalc &&
431,250✔
4174
        pExtW->calcWithPartition &&
59,630✔
4175
        pBlock->info.id.groupId != 0 && pBlock->info.id.baseGId == 0) {
59,630✔
4176
      qDebug("%s skip block: no matched trigger group for groupId %" PRIu64,
8,455✔
4177
             GET_TASKID(pTaskInfo), pBlock->info.id.groupId);
4178
      continue;
8,455✔
4179
    }
4180

4181
    if (pTaskInfo->pStreamRuntimeInfo &&
4,115,375✔
4182
        pTaskInfo->pStreamRuntimeInfo->funcInfo.isMultiGroupCalc &&
422,795✔
4183
        pExtW->calcWithPartition &&
51,175✔
4184
        pBlock->info.id.groupId != 0 && pBlock->info.id.baseGId != 0 &&
51,175✔
4185
        pBlock->info.id.groupId != pBlock->info.id.baseGId) {
51,175✔
4186
      qDebug("%s skip block: baseGId %" PRIu64 " != groupId %" PRIu64,
19,135✔
4187
             GET_TASKID(pTaskInfo), pBlock->info.id.baseGId, pBlock->info.id.groupId);
4188
      continue;
19,135✔
4189
    }
4190

4191
    TAOS_CHECK_EXIT(extWinSwitchInitCtxs(pExtW, pTaskInfo, &pBlock->info.id));
4,096,240✔
4192

4193
    // Reset block-local traversal state for each new input block, but preserve
4194
    // blkWinStartIdx so a window spanning multiple blocks can continue from the
4195
    // same logical window on the next block.
4196
    extWinResetBlockCalcState(pExtW->pTGrpCtx ? pExtW->pTGrpCtx->pCCtx : NULL);
4,096,240✔
4197

4198
    switch (pExtW->mode) {
4,096,240✔
4199
      case EEXT_MODE_SCALAR:
40,050✔
4200
        TAOS_CHECK_EXIT(extWinProjectOpen(pOperator, pBlock));
40,050✔
4201
        if (extWinNonAggGotResBlock(pOperator, pExtW)) {
40,050✔
4202
          goto _exit;
2,670✔
4203
        }
4204
        break;
37,380✔
4205
      case EEXT_MODE_AGG:
4,056,190✔
4206
        TAOS_CHECK_EXIT(extWinAggOpen(pOperator, pBlock));
4,056,190✔
4207
        break;
4,056,190✔
4208
      case EEXT_MODE_INDEFR_FUNC:
×
4209
        TAOS_CHECK_EXIT(extWinIndefRowsOpen(pOperator, pBlock));
×
4210
        if (extWinNonAggGotResBlock(pOperator, pExtW)) {
×
4211
          goto _exit;
×
4212
        }
4213
        break;
×
4214
      default:
×
4215
        break;
×
4216
    }
4217
  }
4218

4219
  if (pOperator->pOperatorGetParam) {
1,710,869✔
4220
    freeOperatorParam(pOperator->pOperatorGetParam, OP_GET_PARAM);
×
4221
    pOperator->pOperatorGetParam = NULL;
×
4222
  }
4223
  OPTR_SET_OPENED(pOperator);
1,710,869✔
4224

4225
  extWinPrepareForOutput(pOperator, pExtW);
1,710,869✔
4226

4227
_exit:
1,713,539✔
4228

4229
  extWinEndClearCtxs(pExtW, pTaskInfo);
1,713,539✔
4230

4231
  if (code != 0) {
1,713,539✔
4232
    qError("%s failed at line %d since:%s", __func__, lino, tstrerror(code));
×
4233
    pTaskInfo->code = code;
×
4234
    T_LONG_JMP(pTaskInfo->env, code);
×
4235
  }
4236

4237
  return code;
1,713,539✔
4238
}
4239

4240
static int32_t extWinNext(SOperatorInfo* pOperator, SSDataBlock** ppRes) {
6,466,253✔
4241
  int32_t                  code = 0;
6,466,253✔
4242
  int32_t                  lino = 0;
6,466,253✔
4243
  SExternalWindowOperator* pExtW = pOperator->info;
6,466,253✔
4244
  SExecTaskInfo*           pTaskInfo = pOperator->pTaskInfo;
6,466,698✔
4245
  if (pOperator->status == OP_EXEC_DONE && !pOperator->pOperatorGetParam) {
6,466,253✔
4246
    *ppRes = NULL;
×
4247
    return code;
×
4248
  }
4249

4250
  if (pOperator->pOperatorGetParam) {
6,466,698✔
4251
    if (pOperator->status == OP_EXEC_DONE) {
1,211,964✔
4252
      pOperator->status = OP_NOT_OPENED;
46,253✔
4253
    }
4254
  }
4255

4256
  extWinRecycleBlkNode(pExtW, &pExtW->pLastBlkNode);
6,466,698✔
4257

4258
  while (1) {
4259
    if (pOperator->status == OP_NOT_OPENED) {
6,468,033✔
4260
      TAOS_CHECK_EXIT(pOperator->fpSet._openFn(pOperator));
1,714,887✔
4261
    }
4262

4263
    if (pExtW->mode == EEXT_MODE_SCALAR || pExtW->mode == EEXT_MODE_INDEFR_FUNC) {
6,466,685✔
4264
      TAOS_CHECK_EXIT(extWinNonAggOutputRes(pOperator, ppRes));
4,566,590✔
4265
      if (NULL == *ppRes) {
4,567,035✔
4266
        setOperatorCompleted(pOperator);
58,295✔
4267
        extWinFreeResultRow(pExtW);
58,295✔
4268
      }
4269
    } else {
4270
      TAOS_CHECK_EXIT(extWinAggOutputRes(pOperator, ppRes));
1,900,095✔
4271
      if (NULL != *ppRes) {
1,900,095✔
4272
        break;
1,459,027✔
4273
      }
4274

4275
      if (pExtW->pTGrpCtx == NULL || pExtW->pTGrpCtx->pCCtx == NULL ||
441,068✔
4276
          (pExtW->isDynWindow ? (pExtW->pTGrpCtx->pCCtx->outWinIdx >= taosArrayGetSize(pExtW->pTGrpCtx->pCCtx->pWins))
154,800✔
4277
                              : (pExtW->pTGrpCtx->pCCtx->outWinNum >= pExtW->pTGrpCtx->pCCtx->outWinTotalNum))) {
154,800✔
4278
        setOperatorCompleted(pOperator);
441,068✔
4279
        if (pTaskInfo->pStreamRuntimeInfo) {
441,068✔
4280
          extWinFreeResultRow(pExtW);
441,068✔
4281
        }
4282
        break;
441,068✔
4283
      }
4284
    }
4285
    break;
4,567,035✔
4286
  }
4287

4288
  if (*ppRes && (*ppRes)->info.rows > 0) {
6,467,130✔
4289
    qDebug("%s tgrp %" PRIu64 " cgrp %" PRIu64 " ext window return block with %" PRId64 " rows", 
5,967,767✔
4290
      GET_TASKID(pTaskInfo), pExtW->lastTGrpId, pExtW->lastCGrpId, (*ppRes)->info.rows);
4291
        
4292
    pOperator->resultInfo.totalRows += (*ppRes)->info.rows;
5,967,767✔
4293
    printDataBlock(*ppRes, __func__, GET_TASKID(pTaskInfo), pTaskInfo->id.queryId);
5,967,767✔
4294
  }
4295

4296
_exit:
499,363✔
4297

4298
  if (code) {
6,467,130✔
4299
    qError("%s %s failed at line %d since %s", GET_TASKID(pTaskInfo), __func__, lino, tstrerror(code));
×
4300
    pTaskInfo->code = code;
×
4301
    T_LONG_JMP(pTaskInfo->env, code);
×
4302
  }
4303

4304
  if ((*ppRes) && (*ppRes)->info.rows <= 0) {
6,467,130✔
4305
    *ppRes = NULL;
×
4306
  }
4307

4308
  if (pTaskInfo->execModel == OPTR_EXEC_MODEL_STREAM && (*ppRes)) {
6,467,130✔
4309
    printDataBlock(*ppRes, getStreamOpName(pOperator->operatorType), GET_TASKID(pTaskInfo), pTaskInfo->id.queryId);
×
4310
  }
4311

4312
  return code;
6,467,130✔
4313
}
4314

4315
static int32_t extWinInitNonStreamWindowDataFromBlock(SExternalWindowPhysiNode* pPhynode, SExecTaskInfo* pTaskInfo, STimeWindow* pTimeRange);
4316
static int32_t extWinValidateNonStreamBlock(SSDataBlock* pBlock, SColumnInfoData** ppStartCol,
4317
                                            SColumnInfoData** ppEndCol, int32_t* pNumRows, int32_t* pNumCols);
4318
static int32_t extWinCheckMonotonicWstart(bool hasPrevStart, int64_t prevStart, int64_t currStart, int32_t row);
4319
static int32_t extWinBuildExternalWindowDataForRow(SSDataBlock* pBlock, int32_t numCols, int32_t row,
4320
                                                   SArray** ppExternalData);
4321
static int32_t extWinBuildTriggerParamForRow(SSDataBlock* pBlock, SColumnInfoData* pStartCol, SColumnInfoData* pEndCol,
4322
                                             int32_t numCols, int32_t row, SSTriggerCalcParam* pParam);
4323

4324
int32_t createExternalWindowOperator(SOperatorInfo* pDownstream, SPhysiNode* pNode, SExecTaskInfo* pTaskInfo,
1,668,220✔
4325
                                     SOperatorInfo** pOptrOut) {
4326
  SExternalWindowPhysiNode* pPhynode = (SExternalWindowPhysiNode*)pNode;
1,668,220✔
4327
  QRY_PARAM_CHECK(pOptrOut);
1,668,220✔
4328
  int32_t                  code = 0;
1,667,775✔
4329
  int32_t                  lino = 0;
1,667,775✔
4330
  bool                     isInStream = true;
1,667,775✔
4331
  STimeWindow              nonStreamExtWinRange = {.skey = INT64_MAX, .ekey = INT64_MIN};
1,667,775✔
4332
  SExternalWindowOperator* pExtW = taosMemoryCalloc(1, sizeof(SExternalWindowOperator));
1,668,220✔
4333
  SOperatorInfo*           pOperator = taosMemoryCalloc(1, sizeof(SOperatorInfo));
1,667,775✔
4334
  pOperator->pPhyNode = pNode;
1,667,330✔
4335
  if (!pExtW || !pOperator) {
1,667,330✔
UNCOV
4336
    code = terrno;
×
4337
    lino = __LINE__;
×
4338
    goto _error;
×
4339
  }
4340
  initOperatorCostInfo(pOperator);
1,667,775✔
4341
  pExtW->pTaskInfo = pTaskInfo;
1,667,775✔
4342
  pExtW->needGroupSort = pPhynode->needGroupSort;
1,667,775✔
4343
  // In non-stream (batch) mode, temporarily disable calcWithPartition to tolerate
4344
  // upstream that does not provide distinct C-group ids (groupId==baseGId).
4345
  // Caller may decide to turn it back on once upstream is ready.
4346
  pExtW->calcWithPartition = pPhynode->calcWithPartition;
1,667,330✔
4347
  // pExtW->calcWithPartition = (pTaskInfo->execModel == OPTR_EXEC_MODEL_STREAM) ? pPhynode->calcWithPartition : false;
4348
  pExtW->extWinSplit = pPhynode->extWinSplit;
1,668,220✔
4349
  
4350
  setOperatorInfo(pOperator, "ExternalWindowOperator", QUERY_NODE_PHYSICAL_PLAN_EXTERNAL_WINDOW, true, OP_NOT_OPENED,
1,667,330✔
4351
                  pExtW, pTaskInfo);
4352
                  
4353
  SSDataBlock* pResBlock = createDataBlockFromDescNode(pPhynode->window.node.pOutputDataBlockDesc);
1,667,330✔
4354
  QUERY_CHECK_NULL(pResBlock, code, lino, _error, terrno);
1,668,220✔
4355
  initBasicInfo(&pExtW->binfo, pResBlock);
1,668,220✔
4356

4357
  pExtW->primaryTsIndex = ((SColumnNode*)pPhynode->window.pTspk)->slotId;
1,668,220✔
4358
  pExtW->mode = pPhynode->window.indefRowsFunc ? EEXT_MODE_INDEFR_FUNC : (pPhynode->window.pFuncs ? EEXT_MODE_AGG : EEXT_MODE_SCALAR);
1,668,220✔
4359
  pExtW->fillMode = pPhynode->extFill.mode;
1,668,220✔
4360
  pExtW->pFillExprs = pPhynode->extFill.pFillExprs;
1,667,775✔
4361
  pExtW->pFillValues = pPhynode->extFill.pFillValues;
1,668,220✔
4362
  pExtW->binfo.inputTsOrder = pPhynode->window.node.inputTsOrder = TSDB_ORDER_ASC;
1,668,220✔
4363
  pExtW->binfo.outputTsOrder = pExtW->binfo.inputTsOrder;
1,668,220✔
4364
  pExtW->isDynWindow = false;
1,667,775✔
4365

4366
  qDebug("%s create extWin operator, execModel:%d, phySubquery:%p", GET_TASKID(pTaskInfo), pTaskInfo->execModel,
1,668,220✔
4367
        pPhynode->pSubquery);
4368

4369
  if (pTaskInfo->pStreamRuntimeInfo != NULL){
1,668,665✔
4370
    pTaskInfo->pStreamRuntimeInfo->funcInfo.withExternalWindow = true;
×
4371
  }
4372
  
4373
  // pExtW->limitInfo = (SLimitInfo){0};
4374
  // initLimitInfo(pPhynode->window.node.pLimit, pPhynode->window.node.pSlimit, &pExtW->limitInfo);
4375

4376
  if (pTaskInfo->execModel != OPTR_EXEC_MODEL_STREAM) {
1,668,220✔
4377
    isInStream = false;
1,668,220✔
4378
    // If pre-init has not been performed, initialize from subquery blocks here.
4379
    if (pTaskInfo->pStreamRuntimeInfo == NULL ||
1,668,220✔
4380
        pTaskInfo->pStreamRuntimeInfo->funcInfo.pGroupCalcInfos == NULL) {
×
4381
      code = extWinInitNonStreamWindowDataFromBlock(pPhynode, pTaskInfo, &nonStreamExtWinRange);
1,668,220✔
4382
      if (code != TSDB_CODE_SUCCESS) {
1,668,220✔
4383
        lino = __LINE__;
890✔
4384
        goto _error;
890✔
4385
      }
4386
    }
4387
  }
4388

4389
  if (pExtW->mode == EEXT_MODE_SCALAR) {
1,667,330✔
4390
    int32_t    numOfScalarExpr = 0;
59,185✔
4391
    SExprInfo* pScalarExprInfo = NULL;
59,185✔
4392
    code = createExprInfo(pPhynode->window.pProjs, NULL, &pScalarExprInfo, &numOfScalarExpr);
59,185✔
4393
    QUERY_CHECK_CODE(code, lino, _error);
59,185✔
4394

4395
    code = initExprSupp(&pExtW->scalarSupp, pScalarExprInfo, numOfScalarExpr, &pTaskInfo->storageAPI.functionStore);
59,185✔
4396
    QUERY_CHECK_CODE(code, lino, _error);
59,185✔
4397

4398
    pExtW->pOutputBlocks = taosArrayInit(STREAM_CALC_REQ_MAX_WIN_NUM, POINTER_BYTES);
59,185✔
4399
    if (!pExtW->pOutputBlocks) QUERY_CHECK_CODE(terrno, lino, _error);
59,185✔
4400

4401
    pExtW->pFreeBlocks = tdListNew(POINTER_BYTES * 2);
59,185✔
4402
    QUERY_CHECK_NULL(pExtW->pFreeBlocks, code, lino, _error, terrno);
59,185✔
4403
  } else if (pExtW->mode == EEXT_MODE_AGG) {
1,608,145✔
4404
    if (pPhynode->window.pExprs != NULL) {
1,608,145✔
4405
      int32_t    num = 0;
7,120✔
4406
      SExprInfo* pSExpr = NULL;
7,120✔
4407
      code = createExprInfo(pPhynode->window.pExprs, NULL, &pSExpr, &num);
7,120✔
4408
      QUERY_CHECK_CODE(code, lino, _error);
7,120✔
4409
    
4410
      code = initExprSupp(&pExtW->scalarSupp, pSExpr, num, &pTaskInfo->storageAPI.functionStore);
7,120✔
4411
      if (code != TSDB_CODE_SUCCESS) {
7,120✔
4412
        goto _error;
×
4413
      }
4414
      checkIndefRowsFuncs(&pExtW->scalarSupp);
7,120✔
4415
    }
4416

4417
    if (pPhynode->window.pProjs != NULL) {
1,608,145✔
4418
      code = extWinInitExprSupp(&pExtW->projSupp, pPhynode->window.pProjs, pTaskInfo);
2,712✔
4419
      QUERY_CHECK_CODE(code, lino, _error);
2,712✔
4420
    }
4421
    
4422
    size_t keyBufSize = sizeof(int64_t) * 2 + POINTER_BYTES;
1,608,145✔
4423
    initResultSizeInfo(&pOperator->resultInfo, 4096);
1,608,145✔
4424
    //code = blockDataEnsureCapacity(pExtW->binfo.pRes, pOperator->resultInfo.capacity);
4425
    //QUERY_CHECK_CODE(code, lino, _error);
4426

4427
    pExtW->pWinRowIdx = taosArrayInit(4096, sizeof(int64_t));
1,608,145✔
4428
    TSDB_CHECK_NULL(pExtW->pWinRowIdx, code, lino, _error, terrno);
1,608,145✔
4429
    
4430
    int32_t num = 0;
1,608,145✔
4431
    SExprInfo* pExprInfo = NULL;
1,608,145✔
4432
    code = createExprInfo(pPhynode->window.pFuncs, NULL, &pExprInfo, &num);
1,608,145✔
4433
    QUERY_CHECK_CODE(code, lino, _error);
1,608,145✔
4434
    pOperator->exprSupp.hasWindow = true;
1,608,145✔
4435
    pOperator->exprSupp.hasWindowOrGroup = true;
1,608,145✔
4436
    code = initAggSup(&pOperator->exprSupp, &pExtW->aggSup, pExprInfo, num, keyBufSize, pTaskInfo->id.str, 0, 0);
1,607,700✔
4437
    QUERY_CHECK_CODE(code, lino, _error);
1,607,700✔
4438

4439
    code = filterInitFromNode((SNode*)pNode->pConditions, &pOperator->exprSupp.pFilterInfo, 0,
1,607,700✔
4440
                              pTaskInfo->pStreamRuntimeInfo);
1,607,700✔
4441
    QUERY_CHECK_CODE(code, lino, _error);
1,607,700✔
4442

4443
    code = extWinBuildFillMatchInfo(&pExtW->fillMatchInfo, pPhynode->extFill.pFillExprs);
1,607,700✔
4444
    QUERY_CHECK_CODE(code, lino, _error);
1,606,810✔
4445

4446
    if (pExtW->fillMode != FILL_MODE_NONE) {
1,606,810✔
4447
      pExtW->pFillRowBuf = taosMemoryMalloc(pExtW->aggSup.resultRowSize);
49,892✔
4448
      QUERY_CHECK_NULL(pExtW->pFillRowBuf, code, lino, _error, terrno);
49,447✔
4449
    }
4450

4451
    nodesWalkExprs(pPhynode->window.pFuncs, extWinHasCountLikeFunc, &pExtW->hasCountFunc);
1,606,810✔
4452
    if (pExtW->fillMode != FILL_MODE_NONE || (pExtW->hasCountFunc && pTaskInfo->execModel == OPTR_EXEC_MODEL_STREAM)) {
1,607,700✔
4453
      code = extWinCreateEmptyInputBlock(pOperator, &pExtW->pEmptyInputBlock);
49,892✔
4454
      QUERY_CHECK_CODE(code, lino, _error);
49,892✔
4455
      qDebug("%s ext window prepared empty input block, fillMode:%d", pOperator->pTaskInfo->id.str, pExtW->fillMode);
49,892✔
4456
    } else {
4457
      qDebug("%s ext window have CountLikeFunc: %d. IsInStream:%d", pOperator->pTaskInfo->id.str, pExtW->hasCountFunc,
1,557,808✔
4458
             pTaskInfo->execModel == OPTR_EXEC_MODEL_STREAM);
4459
    }
4460

4461
    code = initExecTimeWindowInfo(&pExtW->twAggSup.timeWindowData, &pTaskInfo->window);
1,608,145✔
4462
    QUERY_CHECK_CODE(code, lino, _error);
1,608,145✔
4463
  } else {
4464
    size_t  keyBufSize = sizeof(int64_t) + sizeof(int64_t) + POINTER_BYTES;
×
4465
    
4466
    if (pPhynode->window.pExprs != NULL) {
×
4467
      int32_t    num = 0;
×
4468
      SExprInfo* pSExpr = NULL;
×
4469
      code = createExprInfo(pPhynode->window.pExprs, NULL, &pSExpr, &num);
×
4470
      QUERY_CHECK_CODE(code, lino, _error);
×
4471
    
4472
      code = initExprSupp(&pExtW->scalarSupp, pSExpr, num, &pTaskInfo->storageAPI.functionStore);
×
4473
      if (code != TSDB_CODE_SUCCESS) {
×
4474
        goto _error;
×
4475
      }
4476
    }
4477
    
4478
    int32_t    numOfExpr = 0;
×
4479
    SExprInfo* pExprInfo = NULL;
×
4480
    code = createExprInfo(pPhynode->window.pFuncs, NULL, &pExprInfo, &numOfExpr);
×
4481
    TSDB_CHECK_CODE(code, lino, _error);
×
4482
    
4483
    code = initAggSup(&pOperator->exprSupp, &pExtW->aggSup, pExprInfo, numOfExpr, keyBufSize, pTaskInfo->id.str,
×
4484
                              NULL, &pTaskInfo->storageAPI.functionStore);
4485
    TSDB_CHECK_CODE(code, lino, _error);
×
4486
    pOperator->exprSupp.hasWindowOrGroup = false;
×
4487
    
4488
    code = filterInitFromNode((SNode*)pNode->pConditions, &pOperator->exprSupp.pFilterInfo, 0,
×
4489
                              pTaskInfo->pStreamRuntimeInfo);
×
4490
    TSDB_CHECK_CODE(code, lino, _error);
×
4491
    
4492
    pExtW->binfo.inputTsOrder = pNode->inputTsOrder;
×
4493
    pExtW->binfo.outputTsOrder = pNode->outputTsOrder;
×
4494
    code = setRowTsColumnOutputInfo(pOperator->exprSupp.pCtx, numOfExpr, &pExtW->pPseudoColInfo);
×
4495
    TSDB_CHECK_CODE(code, lino, _error);
×
4496

4497
    pExtW->pOutputBlocks = taosArrayInit(STREAM_CALC_REQ_MAX_WIN_NUM, POINTER_BYTES);
×
4498
    if (!pExtW->pOutputBlocks) QUERY_CHECK_CODE(terrno, lino, _error);
×
4499

4500
    pExtW->pFreeBlocks = tdListNew(POINTER_BYTES * 2);
×
4501
    QUERY_CHECK_NULL(pExtW->pFreeBlocks, code, lino, _error, terrno);  
×
4502
  }
4503

4504
  pExtW->timeRangeExpr = (STimeRangeNode*)pPhynode->pTimeRange;
1,667,330✔
4505
  if (pExtW->timeRangeExpr) {
1,667,330✔
4506
    QUERY_CHECK_NULL(pExtW->timeRangeExpr->pStart, code, lino, _error, TSDB_CODE_STREAM_INTERNAL_ERROR);
×
4507
    QUERY_CHECK_NULL(pExtW->timeRangeExpr->pEnd, code, lino, _error, TSDB_CODE_STREAM_INTERNAL_ERROR);
×
4508
  }
4509

4510
  if (pPhynode->isSingleTable) {
1,667,330✔
4511
    if (!isInStream) {
1,021,447✔
4512
      pExtW->getWinFp = extWinGetOvlpWin;
1,021,447✔
4513
      pExtW->multiTableMode = false;
1,021,447✔
4514
    } else {
4515
      pExtW->getWinFp =
×
4516
          (pExtW->timeRangeExpr && (pExtW->timeRangeExpr->needCalc ||
×
4517
                                    (pTaskInfo->pStreamRuntimeInfo->funcInfo.addOptions & CALC_SLIDING_OVERLAP)))
×
4518
              ? extWinGetOvlpWin
4519
              : extWinGetNoOvlpWin;
×
4520
      pExtW->multiTableMode = false;
×
4521
    }
4522
  } else {
4523
    if (!isInStream) {
645,883✔
4524
      pExtW->getWinFp = extWinGetMultiTbOvlpWin;
645,883✔
4525
      pExtW->multiTableMode = true;
645,883✔
4526
    } else {
4527
      pExtW->getWinFp =
×
4528
          (pExtW->timeRangeExpr && (pExtW->timeRangeExpr->needCalc ||
×
4529
                                    (pTaskInfo->pStreamRuntimeInfo->funcInfo.addOptions & CALC_SLIDING_OVERLAP)))
×
4530
              ? extWinGetMultiTbOvlpWin
4531
              : extWinGetMultiTbNoOvlpWin;
×
4532
      pExtW->multiTableMode = true;
×
4533
    }
4534
  }
4535
  pExtW->inputHasOrder = pPhynode->inputHasOrder;
1,667,330✔
4536
  pExtW->orgTableUid = pPhynode->orgTableUid;
1,667,330✔
4537
  pExtW->orgTableVgId = pPhynode->orgTableVgId;
1,667,330✔
4538

4539
  code = extWinInitResRows(pExtW, pTaskInfo);
1,667,330✔
4540
  TSDB_CHECK_CODE(code, lino, _error);
1,667,330✔
4541

4542
  pOperator->fpSet = createOperatorFpSet(extWinOpen, extWinNext, NULL, destroyExternalWindowOperatorInfo,
1,667,330✔
4543
                                         optrDefaultBufFn, NULL, optrDefaultGetNextExtFn, NULL);
4544
  setOperatorResetStateFn(pOperator, resetExternalWindowOperator);
1,667,330✔
4545
  code = appendDownstream(pOperator, &pDownstream, 1);
1,667,330✔
4546
  if (code != 0) {
1,667,330✔
4547
    goto _error;
×
4548
  }
4549

4550
  if (!isInStream) {
1,667,330✔
4551
    code = extWinApplyNonStreamTimeRangeToDownstream(pOperator, &nonStreamExtWinRange);
1,667,330✔
4552
    QUERY_CHECK_CODE(code, lino, _error);
1,667,330✔
4553
  }
4554

4555
  *pOptrOut = pOperator;
1,667,330✔
4556

4557
  qDebug("%s extWin operator created, mode:%s, multiTableMnode:%d, inputHasOrder:%d, hasTimeRangeExpr:%d, timeRangeNeedCalc:%d "
1,667,330✔
4558
    "needGroupSort:%d, extWinSplit:%d, calcWithPartition:%d",
4559
    GET_TASKID(pTaskInfo), extWinModeStr(pExtW->mode), pExtW->multiTableMode, pExtW->inputHasOrder, pExtW->timeRangeExpr ? 1 : 0,
4560
    pExtW->timeRangeExpr ? pExtW->timeRangeExpr->needCalc : -1, pExtW->needGroupSort, pExtW->extWinSplit, pExtW->calcWithPartition);
4561
  
4562
  return code;
1,667,330✔
4563

4564
_error:
890✔
4565

4566
  if (pExtW != NULL) {
890✔
4567
    destroyExternalWindowOperatorInfo(pExtW);
890✔
4568
  }
4569

4570
  destroyOperatorAndDownstreams(pOperator, &pDownstream, 1);
890✔
4571
  pTaskInfo->code = code;
890✔
4572
  qError("error happens at %s %d, code:%s", __func__, lino, tstrerror(code));
890✔
4573
  return code;
890✔
4574
}
4575

4576
// Pre-initialize external-window runtime from subquery results before building children.
4577
// This enables scan operators to see isMultiGroupCalc/curGrpRead and build tablelist with baseGId.
4578
int32_t extWinPreInitFromSubquery(SPhysiNode* pNode, SExecTaskInfo* pTaskInfo) {
×
4579
  if (pNode == NULL || pTaskInfo == NULL) {
×
4580
    return TSDB_CODE_INVALID_PARA;
×
4581
  }
4582
  if (pTaskInfo->execModel == OPTR_EXEC_MODEL_STREAM) {
×
4583
    return TSDB_CODE_SUCCESS;  // stream path unaffected
×
4584
  }
4585
  // If already initialized, skip.
4586
  if (pTaskInfo->pStreamRuntimeInfo &&
×
4587
      pTaskInfo->pStreamRuntimeInfo->funcInfo.pGroupCalcInfos != NULL) {
×
4588
    return TSDB_CODE_SUCCESS;
×
4589
  }
4590
  SExternalWindowPhysiNode* pPhynode = (SExternalWindowPhysiNode*)pNode;
×
4591
  STimeWindow tmpRange = {.skey = INT64_MAX, .ekey = INT64_MIN};
×
4592
  return extWinInitNonStreamWindowDataFromBlock(pPhynode, pTaskInfo, &tmpRange);
×
4593
}
4594

4595
static int32_t extWinValidateNonStreamBlock(SSDataBlock* pBlock, SColumnInfoData** ppStartCol,
674,693✔
4596
                                            SColumnInfoData** ppEndCol, int32_t* pNumRows, int32_t* pNumCols) {
4597
  int32_t code = TSDB_CODE_SUCCESS;
674,693✔
4598
  int32_t lino = 0;
674,693✔
4599

4600
  TSDB_CHECK_NULL(pBlock, code, lino, _exit, TSDB_CODE_INVALID_PARA);
674,693✔
4601
  TSDB_CHECK_NULL(pBlock->pDataBlock, code, lino, _exit, TSDB_CODE_INVALID_PARA);
674,693✔
4602
  TSDB_CHECK_NULL(ppStartCol, code, lino, _exit, TSDB_CODE_INVALID_PARA);
674,693✔
4603
  TSDB_CHECK_NULL(ppEndCol, code, lino, _exit, TSDB_CODE_INVALID_PARA);
674,693✔
4604
  TSDB_CHECK_NULL(pNumRows, code, lino, _exit, TSDB_CODE_INVALID_PARA);
674,693✔
4605
  TSDB_CHECK_NULL(pNumCols, code, lino, _exit, TSDB_CODE_INVALID_PARA);
674,693✔
4606

4607
  int32_t numRows = pBlock->info.rows;
674,693✔
4608
  int32_t numCols = taosArrayGetSize(pBlock->pDataBlock);
674,693✔
4609
  if (numCols < 2) {
674,693✔
4610
    qError("%s invalid external-window block: expected at least 2 columns, got %d", __func__, numCols);
×
4611
    TAOS_CHECK_EXIT(TSDB_CODE_INVALID_PARA);
×
4612
  }
4613

4614
  SColumnInfoData* pStartCol = (SColumnInfoData*)taosArrayGet(pBlock->pDataBlock, 0);
674,693✔
4615
  SColumnInfoData* pEndCol = (SColumnInfoData*)taosArrayGet(pBlock->pDataBlock, 1);
674,693✔
4616
  TSDB_CHECK_NULL(pStartCol, code, lino, _exit, TSDB_CODE_INVALID_PARA);
674,693✔
4617
  TSDB_CHECK_NULL(pEndCol, code, lino, _exit, TSDB_CODE_INVALID_PARA);
674,693✔
4618
  if (pStartCol == NULL || pEndCol == NULL || pStartCol->info.type != TSDB_DATA_TYPE_TIMESTAMP ||
674,693✔
4619
      pEndCol->info.type != TSDB_DATA_TYPE_TIMESTAMP) {
674,693✔
4620
    qError("%s invalid external-window block: first two columns must be timestamp, got (%d, %d)", __func__,
×
4621
           pStartCol ? pStartCol->info.type : -1, pEndCol ? pEndCol->info.type : -1);
4622
    TAOS_CHECK_EXIT(TSDB_CODE_INVALID_PARA);
×
4623
  }
4624
  *ppStartCol = pStartCol;
674,693✔
4625
  *ppEndCol = pEndCol;
674,693✔
4626
  *pNumRows = numRows;
674,693✔
4627
  *pNumCols = numCols;
674,693✔
4628

4629
_exit:
674,693✔
4630
  return code;
674,693✔
4631
}
4632

4633
static int32_t extWinCheckMonotonicWstart(bool hasPrevStart, int64_t prevStart, int64_t currStart, int32_t row) {
59,919,293✔
4634
  if (hasPrevStart && currStart < prevStart) {
59,919,293✔
4635
    qError("%s invalid external-window block: wstart must be monotonic non-decreasing, row:%d, prev:%" PRId64
890✔
4636
           ", curr:%" PRId64,
4637
           __func__, row, prevStart, currStart);
4638
    return TSDB_CODE_EXT_WIN_SUB_UNORDERED;
890✔
4639
  }
4640

4641
  return TSDB_CODE_SUCCESS;
59,918,403✔
4642
}
4643

4644
static void extWinDestroyExternalDataValue(void* ptr) {
×
4645
  SValue* pVal = (SValue*)ptr;
×
4646
  if (pVal != NULL && (IS_VAR_DATA_TYPE(pVal->type) || pVal->type == TSDB_DATA_TYPE_DECIMAL)) {
×
4647
    valueClearDatum(pVal, pVal->type);
×
4648
  }
4649
}
×
4650

4651
static int32_t extWinBuildExternalWindowDataForRow(SSDataBlock* pBlock, int32_t numCols, int32_t row,
60,454,628✔
4652
                                                   SArray** ppExternalData) {
4653
  int32_t code = TSDB_CODE_SUCCESS;
60,454,628✔
4654
  int32_t lino = 0;
60,454,628✔
4655
  SArray* pExternalData = taosArrayInit(numCols - 2, sizeof(SStreamGroupValue));
60,454,628✔
4656
  TSDB_CHECK_NULL(pBlock, code, lino, _exit, TSDB_CODE_INVALID_PARA);
58,921,603✔
4657
  TSDB_CHECK_NULL(pBlock->pDataBlock, code, lino, _exit, TSDB_CODE_INVALID_PARA);
58,921,603✔
4658
  TSDB_CHECK_NULL(ppExternalData, code, lino, _exit, TSDB_CODE_INVALID_PARA);
59,729,723✔
4659
  TSDB_CHECK_NULL(pExternalData, code, lino, _exit, terrno);
59,729,723✔
4660

4661
  for (int32_t col = 2; col < numCols; ++col) {
119,400,261✔
4662
    SColumnInfoData* pColData = (SColumnInfoData*)taosArrayGet(pBlock->pDataBlock, col);
58,358,678✔
4663
    if (pColData == NULL) {
58,724,468✔
4664
      continue;
×
4665
    }
4666

4667
    SStreamGroupValue data = {0};
58,724,468✔
4668
    SValue* pVal = &data.data;
58,766,743✔
4669

4670
    pVal->type = pColData->info.type;
58,766,743✔
4671

4672
    if (!colDataIsNull_s(pColData, row)) {
118,502,696✔
4673
      void* pData = colDataGetData(pColData, row);
59,672,763✔
4674
      if (IS_VAR_DATA_TYPE(pVal->type) || pVal->type == TSDB_DATA_TYPE_DECIMAL) {
59,676,796✔
4675
        int32_t datumLen = IS_VAR_DATA_TYPE(pVal->type) ? calcStrBytesByType(pVal->type, (char*)pData) : pColData->info.bytes;
28✔
4676
        void* pDataCopy = taosMemoryMalloc(datumLen);
73,008✔
4677
        TSDB_CHECK_NULL(pDataCopy, code, lino, _exit, terrno);
73,008✔
4678
        memcpy(pDataCopy, pData, datumLen);
73,008✔
4679
        valueSetDatum(pVal, pVal->type, pDataCopy, datumLen);
73,008✔
4680
      } else {
4681
        valueSetDatum(pVal, pVal->type, pData, pColData->info.bytes);
59,833,380✔
4682
      }
4683
    } else {
4684
      data.isNull = true;
×
4685
    }
4686

4687
    TSDB_CHECK_NULL(taosArrayPush(pExternalData, &data), code, lino, _exit, terrno);
59,688,338✔
4688
  }
4689

4690
  *ppExternalData = pExternalData;
61,041,583✔
4691

4692
_exit:
60,789,713✔
4693
  if (code) {
60,789,713✔
4694
    taosArrayDestroyEx(pExternalData, extWinDestroyExternalDataValue);
×
4695
  }
4696
  return code;
60,907,193✔
4697
}
4698

4699
static int32_t extWinBuildTriggerParamForRow(SSDataBlock* pBlock, SColumnInfoData* pStartCol, SColumnInfoData* pEndCol,
59,303,413✔
4700
                                             int32_t numCols, int32_t row, SSTriggerCalcParam* pParam) {
4701
  int32_t code = TSDB_CODE_SUCCESS;
59,303,413✔
4702
  int32_t lino = 0;
59,303,413✔
4703
  TSDB_CHECK_NULL(pBlock, code, lino, _exit, TSDB_CODE_INVALID_PARA);
59,303,413✔
4704
  TSDB_CHECK_NULL(pStartCol, code, lino, _exit, TSDB_CODE_INVALID_PARA);
59,303,413✔
4705
  TSDB_CHECK_NULL(pEndCol, code, lino, _exit, TSDB_CODE_INVALID_PARA);
59,303,413✔
4706
  TSDB_CHECK_NULL(pParam, code, lino, _exit, TSDB_CODE_INVALID_PARA);
59,303,413✔
4707

4708
  *pParam = (SSTriggerCalcParam){0};
59,303,413✔
4709

4710
  if (!colDataIsNull_s(pStartCol, row)) {
120,461,141✔
4711
    pParam->wstart = *(int64_t*)colDataGetData(pStartCol, row);
60,763,013✔
4712
  }
4713

4714
  if (!colDataIsNull_s(pEndCol, row)) {
122,631,406✔
4715
    pParam->wend = *(int64_t*)colDataGetData(pEndCol, row);
60,919,653✔
4716
  }
4717

4718
  pParam->wduration = pParam->wend - pParam->wstart;
61,288,558✔
4719
  TAOS_CHECK_EXIT(extWinBuildExternalWindowDataForRow(pBlock, numCols, row, &pParam->pExternalWindowData));
61,147,048✔
4720

4721
_exit:
60,048,788✔
4722
  return code;
60,048,788✔
4723
}
4724

4725
#if defined(BUILD_TEST)
4726
static SArray* extWinGetSSDataBlocksInTest(SExternalWindowPhysiNode* pPhynode);
4727
#endif
4728

4729
static uint64_t extWinGetRemoteResultGroupId(const SSDataBlock* pBlock) {
905,336✔
4730
  if (pBlock == NULL) {
905,336✔
4731
    return 0;
×
4732
  }
4733

4734
  return (pBlock->info.id.baseGId != 0) ? pBlock->info.id.baseGId : pBlock->info.id.groupId;
905,336✔
4735
}
4736

4737
static bool hasGroupedRemoteResult(SArray* pBlocks) {
228,863✔
4738
  if (pBlocks == NULL) {
228,863✔
4739
    return false;
×
4740
  }
4741

4742
  int32_t blockNum = taosArrayGetSize(pBlocks);
228,863✔
4743
  for (int32_t blockIdx = 0; blockIdx < blockNum; ++blockIdx) {
382,076✔
4744
    SSDataBlock** ppOne = taosArrayGet(pBlocks, blockIdx);
230,643✔
4745
    if (ppOne == NULL || *ppOne == NULL) {
230,643✔
4746
      continue;
×
4747
    }
4748

4749
    if (extWinGetRemoteResultGroupId(*ppOne) != 0) {
230,643✔
4750
      return true;
77,430✔
4751
    }
4752
  }
4753

4754
  return false;
151,433✔
4755
}
4756

4757
static int32_t extWinAppendNonGroupedCalcParam(SStreamRuntimeFuncInfo* pRt, SSTriggerCalcParam* pParam,
24,613,438✔
4758
                                               bool* pHasPrevStart, int64_t* pPrevStart, int32_t row) {
4759
  int32_t code = TSDB_CODE_SUCCESS;
24,613,438✔
4760

4761
  code = extWinCheckMonotonicWstart(*pHasPrevStart, *pPrevStart, pParam->wstart, row);
24,613,438✔
4762
  if (code != TSDB_CODE_SUCCESS) {
25,045,978✔
4763
    return code;
890✔
4764
  }
4765

4766
  *pPrevStart = pParam->wstart;
25,045,088✔
4767
  *pHasPrevStart = true;
25,061,998✔
4768

4769
  void* pRet = taosArrayPush(pRt->pStreamPesudoFuncVals, pParam);
25,146,548✔
4770
  if (pRet == NULL) {
25,016,608✔
4771
    return terrno;
×
4772
  }
4773

4774
  return TSDB_CODE_SUCCESS;
25,016,608✔
4775
}
4776

4777
static int32_t extWinGetOrCreateGroupedCalcInfo(SStreamRuntimeFuncInfo* pRt, uint64_t groupId,
34,649,925✔
4778
                                                SSTriggerGroupCalcInfo** ppGroupInfo) {
4779
  int32_t code = TSDB_CODE_SUCCESS;
34,649,925✔
4780
  int32_t lino = 0;
34,649,925✔
4781
  SSTriggerGroupCalcInfo* pGroupInfo = tSimpleHashGet(pRt->pGroupCalcInfos, &groupId, sizeof(groupId));
34,649,925✔
4782

4783
  if (pGroupInfo == NULL) {
35,562,620✔
4784
    SSTriggerGroupCalcInfo info = {0};
153,525✔
4785
    info.pParams = taosArrayInit(4, sizeof(SSTriggerCalcParam));
153,525✔
4786
    TSDB_CHECK_NULL(info.pParams, code, lino, _exit, terrno);
153,525✔
4787

4788
    TAOS_CHECK_EXIT(tSimpleHashPut(pRt->pGroupCalcInfos, &groupId, sizeof(groupId), &info, sizeof(info)));
153,525✔
4789

4790
    pGroupInfo = tSimpleHashGet(pRt->pGroupCalcInfos, &groupId, sizeof(groupId));
153,525✔
4791
    TSDB_CHECK_NULL(pGroupInfo, code, lino, _exit, TSDB_CODE_QRY_EXECUTOR_INTERNAL_ERROR);
153,525✔
4792
  }
4793

4794
  *ppGroupInfo = pGroupInfo;
35,697,455✔
4795

4796
_exit:
35,390,850✔
4797
  return code;
35,390,850✔
4798
}
4799

4800
static int32_t extWinAppendGroupedCalcParam(SStreamRuntimeFuncInfo* pRt, uint64_t groupId, SSTriggerCalcParam* pParam,
34,769,185✔
4801
                                            int32_t row) {
4802
  int32_t code = TSDB_CODE_SUCCESS;
34,769,185✔
4803
  int32_t lino = 0;
34,769,185✔
4804
  SSTriggerGroupCalcInfo* pGroupInfo = NULL;
34,769,185✔
4805
  bool groupHasPrevStart = false;
34,829,260✔
4806
  int64_t groupPrevStart = 0;
34,829,260✔
4807

4808
  TAOS_CHECK_EXIT(extWinGetOrCreateGroupedCalcInfo(pRt, groupId, &pGroupInfo));
34,829,260✔
4809

4810
  groupHasPrevStart = (taosArrayGetSize(pGroupInfo->pParams) > 0);
35,421,555✔
4811
  if (groupHasPrevStart) {
35,271,590✔
4812
    SSTriggerCalcParam* pLast = taosArrayGetLast(pGroupInfo->pParams);
35,198,610✔
4813
    TSDB_CHECK_NULL(pLast, code, lino, _exit, TSDB_CODE_QRY_EXECUTOR_INTERNAL_ERROR);
35,435,350✔
4814
    groupPrevStart = pLast->wstart;
35,435,350✔
4815
  }
4816

4817
  code = extWinCheckMonotonicWstart(groupHasPrevStart, groupPrevStart, pParam->wstart, row);
35,502,990✔
4818
  if (code != TSDB_CODE_SUCCESS) {
35,478,960✔
4819
    TAOS_CHECK_EXIT(code);
×
4820
  }
4821

4822
  TSDB_CHECK_NULL(taosArrayPush(pGroupInfo->pParams, pParam), code, lino, _exit, terrno);
71,106,105✔
4823

4824
_exit:
35,627,145✔
4825
  return code;
35,696,565✔
4826
}
4827

4828
static int32_t extWinBuildNonGroupedCalcInfosFromBlocks(SArray* pBlocks, SStreamRuntimeFuncInfo* pRt,
486,903✔
4829
                                                        STimeWindow* pExtWinTimeRange) {
4830
  int32_t code = TSDB_CODE_SUCCESS;
486,903✔
4831
  int32_t lino = 0;
486,903✔
4832
  SColumnInfoData* pStartCol = NULL;
486,903✔
4833
  SColumnInfoData* pEndCol = NULL;
486,903✔
4834
  int32_t numRows = 0;
486,903✔
4835
  int32_t numCols = 0;
486,903✔
4836
  bool hasPrevStart = false;
486,903✔
4837
  int64_t prevStart = 0;
486,903✔
4838
  uint64_t prevGroupId = UINT64_MAX;
486,903✔
4839
  int32_t blockNum = taosArrayGetSize(pBlocks);
486,903✔
4840

4841
  for (int32_t blockIdx = 0; blockIdx < blockNum; ++blockIdx) {
978,701✔
4842
    SSDataBlock** ppOne = taosArrayGet(pBlocks, blockIdx);
492,688✔
4843
    TSDB_CHECK_NULL(ppOne, code, lino, _exit, TSDB_CODE_INVALID_PARA);
492,688✔
4844
    SSDataBlock* pBlock = *ppOne;
492,688✔
4845
    TSDB_CHECK_NULL(pBlock, code, lino, _exit, TSDB_CODE_INVALID_PARA);
492,688✔
4846

4847
    TAOS_CHECK_EXIT(extWinValidateNonStreamBlock(pBlock, &pStartCol, &pEndCol, &numRows, &numCols));
492,688✔
4848

4849
    uint64_t groupId = extWinGetRemoteResultGroupId(pBlock);
492,688✔
4850
    if (prevGroupId != groupId) {
492,688✔
4851
      hasPrevStart = false;
484,233✔
4852
      prevGroupId = groupId;
484,233✔
4853
    }
4854

4855
    for (int32_t row = 0; row < numRows; ++row) {
25,462,126✔
4856
      SSTriggerCalcParam param = {0};
24,985,013✔
4857
      TAOS_CHECK_EXIT(extWinBuildTriggerParamForRow(pBlock, pStartCol, pEndCol, numCols, row, &param));
24,960,093✔
4858

4859
      pExtWinTimeRange->skey = TMIN(pExtWinTimeRange->skey, param.wstart);
24,620,558✔
4860
      pExtWinTimeRange->ekey = TMAX(pExtWinTimeRange->ekey, param.wend);
24,766,518✔
4861

4862
      code = extWinAppendNonGroupedCalcParam(pRt, &param, &hasPrevStart, &prevStart, row);
25,109,168✔
4863
      if (code != TSDB_CODE_SUCCESS) {
24,993,023✔
4864
        tDestroySSTriggerCalcParam(&param);
890✔
4865
        TAOS_CHECK_EXIT(code);
890✔
4866
      }
4867
    }
4868
  }
4869

4870
_exit:
469,993✔
4871
  return code;
486,903✔
4872
}
4873

4874
static int32_t extWinBuildGroupedCalcInfosFromBlocks(SArray* pBlocks, SStreamRuntimeFuncInfo* pRt,
77,430✔
4875
                                                     STimeWindow* pExtWinTimeRange) {
4876
  int32_t code = TSDB_CODE_SUCCESS;
77,430✔
4877
  int32_t lino = 0;
77,430✔
4878
  SColumnInfoData* pStartCol = NULL;
77,430✔
4879
  SColumnInfoData* pEndCol = NULL;
77,430✔
4880
  int32_t numRows = 0;
77,430✔
4881
  int32_t numCols = 0;
77,430✔
4882
  int32_t blockNum = taosArrayGetSize(pBlocks);
77,430✔
4883

4884
  for (int32_t blockIdx = 0; blockIdx < blockNum; ++blockIdx) {
259,435✔
4885
    SSDataBlock** ppOne = taosArrayGet(pBlocks, blockIdx);
182,005✔
4886
    TSDB_CHECK_NULL(ppOne, code, lino, _exit, TSDB_CODE_INVALID_PARA);
182,005✔
4887
    SSDataBlock* pBlock = *ppOne;
182,005✔
4888
    TSDB_CHECK_NULL(pBlock, code, lino, _exit, TSDB_CODE_INVALID_PARA);
182,005✔
4889

4890
    TAOS_CHECK_EXIT(extWinValidateNonStreamBlock(pBlock, &pStartCol, &pEndCol, &numRows, &numCols));
182,005✔
4891

4892
    uint64_t groupId = extWinGetRemoteResultGroupId(pBlock);
182,005✔
4893
    for (int32_t row = 0; row < numRows; ++row) {
35,846,975✔
4894
      SSTriggerCalcParam param = {0};
35,773,550✔
4895
      TAOS_CHECK_EXIT(extWinBuildTriggerParamForRow(pBlock, pStartCol, pEndCol, numCols, row, &param));
35,763,760✔
4896

4897
      pExtWinTimeRange->skey = TMIN(pExtWinTimeRange->skey, param.wstart);
35,350,355✔
4898
      pExtWinTimeRange->ekey = TMAX(pExtWinTimeRange->ekey, param.wend);
35,369,490✔
4899

4900
      code = extWinAppendGroupedCalcParam(pRt, groupId, &param, row);
35,393,075✔
4901
      if (code != TSDB_CODE_SUCCESS) {
35,647,615✔
4902
        tDestroySSTriggerCalcParam(&param);
×
4903
        TAOS_CHECK_EXIT(code);
×
4904
      }
4905
    }
4906
  }
4907

UNCOV
4908
_exit:
×
4909
  return code;
77,430✔
4910
}
4911

4912
static int32_t extWinInitNonStreamWindowDataFromBlock(SExternalWindowPhysiNode* pPhynode, SExecTaskInfo* pTaskInfo, STimeWindow* pTimeRange) {
1,731,410✔
4913
  int32_t code = TSDB_CODE_SUCCESS;
1,731,410✔
4914
  int32_t     lino = 0;
1,731,410✔
4915
  SArray*     pBlocks = NULL;
1,731,410✔
4916
  STimeWindow extWinTimeRange = {.skey = INT64_MAX, .ekey = INT64_MIN};
1,731,410✔
4917

4918
  if (NULL == pPhynode->pSubquery || nodeType(pPhynode->pSubquery) != QUERY_NODE_REMOTE_TABLE) {
1,731,410✔
4919
    qDebug("invalid subquery in external window, pSubquery:%p, type:%d", pPhynode->pSubquery,
1,167,077✔
4920
           pPhynode->pSubquery ? nodeType(pPhynode->pSubquery) : -1);
4921
    return TSDB_CODE_SUCCESS;
1,167,077✔
4922
  }
4923
  TSDB_CHECK_NULL(pTaskInfo, code, lino, _exit, TSDB_CODE_INVALID_PARA);
564,333✔
4924

4925
  if (pTaskInfo->pStreamRuntimeInfo == NULL) {
564,333✔
4926
    pTaskInfo->pStreamRuntimeInfo = (SStreamRuntimeInfo*)taosMemoryCalloc(1, sizeof(SStreamRuntimeInfo));
564,333✔
4927
    TSDB_CHECK_NULL(pTaskInfo->pStreamRuntimeInfo, code, lino, _exit, terrno);
564,333✔
4928
    pTaskInfo->ownStreamRtInfo = true;
564,333✔
4929
  }
4930

4931
  // Initialize basic runtime function parameters.
4932
  SStreamRuntimeFuncInfo* pRt = &pTaskInfo->pStreamRuntimeInfo->funcInfo;
564,333✔
4933
  pRt->withExternalWindow = true;
564,333✔
4934
  pRt->isWindowTrigger = true;
564,333✔
4935
  pRt->triggerType = STREAM_TRIGGER_SESSION;
564,333✔
4936
  pRt->precision = 0;
564,333✔
4937
  pRt->curIdx = 0;
564,333✔
4938

4939
#if defined(BUILD_TEST)
4940
  // Test-only path:
4941
  // 1) route by subquery source db name and build mock external-window blocks;
4942
  // 2) non-test db names are treated as unsupported since production interface is not implemented yet.
4943
  pBlocks = extWinGetSSDataBlocksInTest(pPhynode);
4944
  TSDB_CHECK_NULL(pBlocks, code, lino, _exit, terrno);
4945
#else
4946
  // get the block with external window values from subquery, for now just return error since this code
4947
  // path is only for non-stream query which is not supported yet.
4948
  
4949
  SRemoteTableNode* pRemote = (SRemoteTableNode*)pPhynode->pSubquery;
564,333✔
4950
  TAOS_CHECK_EXIT(qFetchRemoteNode(gTaskScalarExtra.pSubJobCtx, pRemote->subQIdx, (SNode*)pRemote));
564,333✔
4951

4952
  pBlocks = pRemote->pResBlks;
564,333✔
4953
#endif
4954
  // For non-stream external_window, whether trigger windows should be built as
4955
  // grouped calc infos must follow the OUTER query partition semantics instead of
4956
  // raw remote block ids. Remote subquery result blocks may carry non-zero ids
4957
  // even when the subquery is semantically non-grouped, which would incorrectly
4958
  // split one logical external window into multiple TGrps.
4959
  bool groupedRemoteResult = pPhynode->calcWithPartition && hasGroupedRemoteResult(pBlocks);
564,333✔
4960
  pRt->isMultiGroupCalc = groupedRemoteResult ? 1 : 0;
564,333✔
4961
  pRt->curGrpCalc = NULL;
564,333✔
4962
  pRt->groupId = 0;
564,333✔
4963

4964
  // Initialize/reset pseudo function values.
4965
  if (pRt->pStreamPesudoFuncVals == NULL) {
564,333✔
4966
    pRt->pStreamPesudoFuncVals = taosArrayInit(4, sizeof(SSTriggerCalcParam));
564,333✔
4967
    TSDB_CHECK_NULL(pRt->pStreamPesudoFuncVals, code, lino, _exit, terrno);
564,333✔
4968
  } else {
4969
    taosArrayClearEx(pRt->pStreamPesudoFuncVals, tDestroySSTriggerCalcParam);
×
4970
  }
4971

4972
  if (pRt->pGroupCalcInfos != NULL) {
564,333✔
4973
    tSimpleHashCleanup(pRt->pGroupCalcInfos);
×
4974
    pRt->pGroupCalcInfos = NULL;
×
4975
  }
4976

4977
  if (groupedRemoteResult) {
564,333✔
4978
    pRt->pGroupCalcInfos = tSimpleHashInit(256, taosGetDefaultHashFunction(TSDB_DATA_TYPE_UBIGINT));
77,430✔
4979
    TSDB_CHECK_NULL(pRt->pGroupCalcInfos, code, lino, _exit, terrno);
77,430✔
4980
    tSimpleHashSetFreeFp(pRt->pGroupCalcInfos, tDestroySSTriggerGroupCalcInfo);
77,430✔
4981
  }
4982
  
4983
  if (groupedRemoteResult) {
564,333✔
4984
    TAOS_CHECK_EXIT(extWinBuildGroupedCalcInfosFromBlocks(pBlocks, pRt, &extWinTimeRange));
77,430✔
4985
    // The grouped path uses per-group pParams (in pGroupCalcInfos) instead of
4986
    // pStreamPesudoFuncVals.  extWinInitCGrpCtx later overwrites the pointer,
4987
    // orphaning the original SArray.  Free it now while we still can.
4988
    taosArrayDestroy(pRt->pStreamPesudoFuncVals);
77,430✔
4989
    pRt->pStreamPesudoFuncVals = NULL;
77,430✔
4990
  } else {
4991
    TAOS_CHECK_EXIT(extWinBuildNonGroupedCalcInfosFromBlocks(pBlocks, pRt, &extWinTimeRange));
486,903✔
4992
  }
4993

4994
  if (pTimeRange != NULL) {
563,443✔
4995
    *pTimeRange = extWinTimeRange;
563,443✔
4996
  }
4997

4998
  if (!groupedRemoteResult && taosArrayGetSize(pRt->pStreamPesudoFuncVals) > 0) {
563,443✔
4999
    qInfo("%s non-stream extWin mock initialized from block, winNum:%d, firstWin:[%" PRId64 ", %" PRId64 "], wholeRange:[%" PRId64 ", %" PRId64 "]",
483,343✔
5000
          GET_TASKID(pTaskInfo), (int32_t)taosArrayGetSize(pRt->pStreamPesudoFuncVals),
5001
          ((SSTriggerCalcParam*)taosArrayGet(pRt->pStreamPesudoFuncVals, 0))->wstart,
5002
          ((SSTriggerCalcParam*)taosArrayGet(pRt->pStreamPesudoFuncVals, 0))->wend,
5003
          extWinTimeRange.skey, extWinTimeRange.ekey);
5004
  } else if (groupedRemoteResult) {
80,100✔
5005
    qInfo("%s non-stream extWin initialized from grouped remote result, groupNum:%d, wholeRange:[%" PRId64 ", %" PRId64 "]",
77,430✔
5006
          GET_TASKID(pTaskInfo), pRt->pGroupCalcInfos ? tSimpleHashGetSize(pRt->pGroupCalcInfos) : 0,
5007
          extWinTimeRange.skey, extWinTimeRange.ekey);
5008
  }
5009

5010
_exit:
564,333✔
5011
  if (pBlocks) {
564,333✔
5012
    for (int32_t blockIdx = 0; blockIdx < taosArrayGetSize(pBlocks); ++blockIdx) {
1,249,706✔
5013
      SSDataBlock** ppOne = taosArrayGet(pBlocks, blockIdx);
688,043✔
5014
      if (ppOne && *ppOne) {
688,043✔
5015
        blockDataDestroy(*ppOne);
688,043✔
5016
      }
5017
    }
5018
    taosArrayDestroy(pBlocks);
561,663✔
5019
  }
5020
  return code;
564,333✔
5021
}
5022

5023
#if defined(BUILD_TEST)
5024
// mockSSDataBlock is a helper function to create a sample SSDataBlock for testing purposes.
5025
static SSDataBlock* mockSSDataBlock() {
5026
  SSDataBlock* pBlock = NULL;
5027
  int32_t      code = createDataBlock(&pBlock);
5028
  if (code != TSDB_CODE_SUCCESS || pBlock == NULL) {
5029
    return NULL;
5030
  }
5031

5032
  // Add 4 columns: timestamp, timestamp, int, bigint.
5033
  SColumnInfoData col1 = createColumnInfoData(TSDB_DATA_TYPE_TIMESTAMP, 8, 1);
5034
  code = blockDataAppendColInfo(pBlock, &col1);
5035
  if (code != TSDB_CODE_SUCCESS) {
5036
    blockDataDestroy(pBlock);
5037
    return NULL;
5038
  }
5039

5040
  SColumnInfoData col2 = createColumnInfoData(TSDB_DATA_TYPE_TIMESTAMP, 8, 2);
5041
  code = blockDataAppendColInfo(pBlock, &col2);
5042
  if (code != TSDB_CODE_SUCCESS) {
5043
    blockDataDestroy(pBlock);
5044
    return NULL;
5045
  }
5046

5047
  SColumnInfoData col3 = createColumnInfoData(TSDB_DATA_TYPE_INT, 4, 3);
5048
  code = blockDataAppendColInfo(pBlock, &col3);
5049
  if (code != TSDB_CODE_SUCCESS) {
5050
    blockDataDestroy(pBlock);
5051
    return NULL;
5052
  }
5053

5054
  SColumnInfoData col4 = createColumnInfoData(TSDB_DATA_TYPE_BIGINT, 8, 4);
5055
  code = blockDataAppendColInfo(pBlock, &col4);
5056
  if (code != TSDB_CODE_SUCCESS) {
5057
    blockDataDestroy(pBlock);
5058
    return NULL;
5059
  }
5060

5061
  // Ensure capacity for all rows.
5062
  code = blockDataEnsureCapacity(pBlock, 2);
5063
  if (code != TSDB_CODE_SUCCESS) {
5064
    blockDataDestroy(pBlock);
5065
    return NULL;
5066
  }
5067

5068
  // Get column data pointers.
5069
  SColumnInfoData* pCol1 = taosArrayGet(pBlock->pDataBlock, 0);
5070
  SColumnInfoData* pCol2 = taosArrayGet(pBlock->pDataBlock, 1);
5071
  SColumnInfoData* pCol3 = taosArrayGet(pBlock->pDataBlock, 2);
5072
  SColumnInfoData* pCol4 = taosArrayGet(pBlock->pDataBlock, 3);
5073
  if (pCol1 == NULL || pCol2 == NULL || pCol3 == NULL || pCol4 == NULL) {
5074
    blockDataDestroy(pBlock);
5075
    return NULL;
5076
  }
5077

5078
  // Row 1 values.
5079
  int64_t ts1 = 1589335200000;
5080
  int64_t ts2 = 1589338140000;
5081
  int32_t intVal1 = 100;
5082
  int64_t bigintVal1 = 1000000LL;
5083

5084
  code = colDataSetVal(pCol1, 0, (const char*)&ts1, false);
5085
  if (code != TSDB_CODE_SUCCESS) {
5086
    blockDataDestroy(pBlock);
5087
    return NULL;
5088
  }
5089
  code = colDataSetVal(pCol2, 0, (const char*)&ts2, false);
5090
  if (code != TSDB_CODE_SUCCESS) {
5091
    blockDataDestroy(pBlock);
5092
    return NULL;
5093
  }
5094
  code = colDataSetVal(pCol3, 0, (const char*)&intVal1, false);
5095
  if (code != TSDB_CODE_SUCCESS) {
5096
    blockDataDestroy(pBlock);
5097
    return NULL;
5098
  }
5099
  code = colDataSetVal(pCol4, 0, (const char*)&bigintVal1, false);
5100
  if (code != TSDB_CODE_SUCCESS) {
5101
    blockDataDestroy(pBlock);
5102
    return NULL;
5103
  }
5104
  pBlock->info.rows++;
5105

5106
  // Row 2 values.
5107
  int64_t ts3 = 1589338140001;
5108
  int64_t ts4 = 1589340110000;
5109
  int32_t intVal2 = 200;
5110
  int64_t bigintVal2 = 2000000LL;
5111

5112
  code = colDataSetVal(pCol1, 1, (const char*)&ts3, false);
5113
  if (code != TSDB_CODE_SUCCESS) {
5114
    blockDataDestroy(pBlock);
5115
    return NULL;
5116
  }
5117
  code = colDataSetVal(pCol2, 1, (const char*)&ts4, false);
5118
  if (code != TSDB_CODE_SUCCESS) {
5119
    blockDataDestroy(pBlock);
5120
    return NULL;
5121
  }
5122
  code = colDataSetVal(pCol3, 1, (const char*)&intVal2, false);
5123
  if (code != TSDB_CODE_SUCCESS) {
5124
    blockDataDestroy(pBlock);
5125
    return NULL;
5126
  }
5127
  code = colDataSetVal(pCol4, 1, (const char*)&bigintVal2, false);
5128
  if (code != TSDB_CODE_SUCCESS) {
5129
    blockDataDestroy(pBlock);
5130
    return NULL;
5131
  }
5132
  pBlock->info.rows++;
5133

5134
  pBlock->info.id.groupId = 0;
5135
  pBlock->info.id.baseGId = pBlock->info.id.groupId;
5136

5137
  return pBlock;
5138
}
5139

5140
static int32_t extWinMockSSDataBlocksWithGroups(SArray** ppBlocks) {
5141
  int32_t      code = TSDB_CODE_SUCCESS;
5142
  int32_t      lino = 0;
5143
  SArray*      pBlocks = NULL;
5144
  SSDataBlock* pBlock1 = NULL;
5145
  SSDataBlock* pBlock2 = NULL;
5146

5147
  TSDB_CHECK_NULL(ppBlocks, code, lino, _exit, TSDB_CODE_INVALID_PARA);
5148

5149
  pBlocks = taosArrayInit(2, POINTER_BYTES);
5150
  TSDB_CHECK_NULL(pBlocks, code, lino, _exit, terrno);
5151

5152
  pBlock1 = mockSSDataBlock();
5153
  TSDB_CHECK_NULL(pBlock1, code, lino, _exit, terrno);
5154
  pBlock1->info.id.groupId = 1001;
5155
  pBlock1->info.id.baseGId = pBlock1->info.id.groupId;
5156

5157
  pBlock2 = mockSSDataBlock();
5158
  TSDB_CHECK_NULL(pBlock2, code, lino, _exit, terrno);
5159
  pBlock2->info.id.groupId = 1002;
5160
  pBlock2->info.id.baseGId = pBlock2->info.id.groupId;
5161

5162
  SColumnInfoData* pG2Start = taosArrayGet(pBlock2->pDataBlock, 0);
5163
  SColumnInfoData* pG2End = taosArrayGet(pBlock2->pDataBlock, 1);
5164
  SColumnInfoData* pG2Int = taosArrayGet(pBlock2->pDataBlock, 2);
5165
  SColumnInfoData* pG2BigInt = taosArrayGet(pBlock2->pDataBlock, 3);
5166
  TSDB_CHECK_NULL(pG2Start, code, lino, _exit, TSDB_CODE_INVALID_PARA);
5167
  TSDB_CHECK_NULL(pG2End, code, lino, _exit, TSDB_CODE_INVALID_PARA);
5168
  TSDB_CHECK_NULL(pG2Int, code, lino, _exit, TSDB_CODE_INVALID_PARA);
5169
  TSDB_CHECK_NULL(pG2BigInt, code, lino, _exit, TSDB_CODE_INVALID_PARA);
5170

5171
  for (int32_t row = 0; row < pBlock2->info.rows; ++row) {
5172
    int64_t startTs = *(int64_t*)colDataGetData(pG2Start, row);
5173
    int64_t endTs = *(int64_t*)colDataGetData(pG2End, row);
5174
    int32_t intVal = *(int32_t*)colDataGetData(pG2Int, row);
5175
    int64_t bigIntVal = *(int64_t*)colDataGetData(pG2BigInt, row);
5176

5177
    startTs += 3600000;
5178
    endTs += 3600000;
5179
    intVal += 100;
5180
    bigIntVal += 1000000;
5181

5182
    TAOS_CHECK_EXIT(colDataSetVal(pG2Start, row, (const char*)&startTs, false));
5183
    TAOS_CHECK_EXIT(colDataSetVal(pG2End, row, (const char*)&endTs, false));
5184
    TAOS_CHECK_EXIT(colDataSetVal(pG2Int, row, (const char*)&intVal, false));
5185
    TAOS_CHECK_EXIT(colDataSetVal(pG2BigInt, row, (const char*)&bigIntVal, false));
5186
  }
5187

5188
  TSDB_CHECK_NULL(taosArrayPush(pBlocks, &pBlock1), code, lino, _exit, terrno);
5189
  TSDB_CHECK_NULL(taosArrayPush(pBlocks, &pBlock2), code, lino, _exit, terrno);
5190

5191
  *ppBlocks = pBlocks;
5192
  return code;
5193

5194
_exit:
5195
  if (pBlock1) {
5196
    blockDataDestroy(pBlock1);
5197
  }
5198
  if (pBlock2) {
5199
    blockDataDestroy(pBlock2);
5200
  }
5201
  if (pBlocks) {
5202
    taosArrayDestroy(pBlocks);
5203
  }
5204
  return code;
5205
}
5206

5207
typedef enum {
5208
  EXT_WIN_TEST_MOCK_UNSUPPORTED = 0,
5209
  EXT_WIN_TEST_MOCK_SINGLE_BLOCK,
5210
  EXT_WIN_TEST_MOCK_GROUP_BLOCKS,
5211
} EExtWinTestMockMode;
5212

5213
typedef struct {
5214
  EExtWinTestMockMode mode;
5215
} SExtWinTestMockCtx;
5216

5217
static bool extWinDetectTestMockModeFromPhysiNode(SPhysiNode* pNode, SExtWinTestMockCtx* pCtx) {
5218
  if (pNode == NULL || pCtx == NULL) {
5219
    return false;
5220
  }
5221

5222
  if (nodeType(pNode) == QUERY_NODE_PHYSICAL_PLAN_TABLE_SCAN) {
5223
    STableScanPhysiNode* pScan = (STableScanPhysiNode*)pNode;
5224
    const char*          pDbName = tNameGetDbNameP(&pScan->scan.tableName);
5225
    if (pDbName == NULL) {
5226
      pCtx->mode = EXT_WIN_TEST_MOCK_UNSUPPORTED;
5227
      return true;
5228
    }
5229

5230
    if (0 == strcasecmp(pDbName, "external_window_test_single_block")) {
5231
      pCtx->mode = EXT_WIN_TEST_MOCK_SINGLE_BLOCK;
5232
    } else if (0 == strcasecmp(pDbName, "external_window_test_group_blocks")) {
5233
      pCtx->mode = EXT_WIN_TEST_MOCK_GROUP_BLOCKS;
5234
    } else {
5235
      pCtx->mode = EXT_WIN_TEST_MOCK_UNSUPPORTED;
5236
    }
5237

5238
    return true;
5239
  }
5240

5241
  SNode* pChild = NULL;
5242
  FOREACH(pChild, pNode->pChildren) {
5243
    if (extWinDetectTestMockModeFromPhysiNode((SPhysiNode*)pChild, pCtx)) {
5244
      return true;
5245
    }
5246
  }
5247

5248
  return false;
5249
}
5250

5251
static SArray* extWinGetSSDataBlocksInTest(SExternalWindowPhysiNode* pPhynode) {
5252
  int32_t code = TSDB_CODE_SUCCESS;
5253
  int32_t lino = 0;
5254
  if (pPhynode == NULL || pPhynode->pSubquery == NULL) {
5255
    terrno = TSDB_CODE_INVALID_PARA;
5256
    return NULL;
5257
  }
5258

5259
  SExtWinTestMockCtx ctx = {.mode = EXT_WIN_TEST_MOCK_UNSUPPORTED};
5260
  SNode*             pChild = NULL;
5261
  FOREACH(pChild, pPhynode->window.node.pChildren) {
5262
    if (extWinDetectTestMockModeFromPhysiNode((SPhysiNode*)pChild, &ctx)) {
5263
      break;
5264
    }
5265
  }
5266

5267
  if (ctx.mode == EXT_WIN_TEST_MOCK_SINGLE_BLOCK) {
5268
    SArray*      pBlocks = taosArrayInit(1, POINTER_BYTES);
5269
    SSDataBlock* pBlock = NULL;
5270
    if (pBlocks == NULL) {
5271
      return NULL;
5272
    }
5273

5274
    pBlock = mockSSDataBlock();
5275
    if (pBlock == NULL) {
5276
      taosArrayDestroy(pBlocks);
5277
      return NULL;
5278
    }
5279

5280
    if (taosArrayPush(pBlocks, &pBlock) == NULL) {
5281
      blockDataDestroy(pBlock);
5282
      taosArrayDestroy(pBlocks);
5283
      return NULL;
5284
    }
5285

5286
    return pBlocks;
5287
  }
5288

5289
  if (ctx.mode == EXT_WIN_TEST_MOCK_GROUP_BLOCKS) {
5290
    SArray* pBlocks = NULL;
5291
    int32_t code = extWinMockSSDataBlocksWithGroups(&pBlocks);
5292
    TAOS_CHECK_EXIT(code);
5293
    return pBlocks;
5294
  }
5295

5296
    if (NULL == pPhynode->pSubquery || nodeType(pPhynode->pSubquery) != QUERY_NODE_REMOTE_TABLE) {
5297
    qError("invalid subquery in external window, pSubquery:%p, type:%d", pPhynode->pSubquery, pPhynode->pSubquery ? nodeType(pPhynode->pSubquery) : -1);
5298
    code = TSDB_CODE_QRY_EXECUTOR_INTERNAL_ERROR;
5299
    TAOS_CHECK_EXIT(code);
5300
  }
5301
  
5302
  SRemoteTableNode* pRemote = (SRemoteTableNode*)pPhynode->pSubquery;
5303
  TAOS_CHECK_EXIT(qFetchRemoteNode(gTaskScalarExtra.pSubJobCtx, pRemote->subQIdx, (SNode*)pRemote));
5304

5305
  return pRemote->pResBlks;
5306
_exit:
5307
  if (code != TSDB_CODE_SUCCESS) {
5308
    terrno = code;
5309
    qError("%s : %d error code:%s", __func__, lino, tstrerror(code));
5310
  }
5311

5312
  return NULL;
5313
}
5314
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc