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

taosdata / TDengine / #5044

06 May 2026 02:35AM UTC coverage: 73.169% (+0.06%) from 73.107%
#5044

push

travis-ci

web-flow
feat: [6659794715] cpu limit (#35153)

244 of 275 new or added lines in 23 files covered. (88.73%)

526 existing lines in 141 files now uncovered.

277745 of 379596 relevant lines covered (73.17%)

133740972.66 hits per line

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

74.43
/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,441,904✔
154
  switch (mode) {
4,441,904✔
155
    case EEXT_MODE_SCALAR:
97,128✔
156
      return "scalar";
97,128✔
157
    case EEXT_MODE_AGG:
4,344,776✔
158
      return "agg";
4,344,776✔
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) {
852✔
170
  uint64_t* gId1 = (uint64_t*)p1;
852✔
171
  uint64_t* gId2 = (uint64_t*)p2;
852✔
172

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

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

185
  SExtWinCalcGrpCtx* pCtx = (SExtWinCalcGrpCtx*)param;
1,577,020✔
186

187
  taosArrayDestroy(pCtx->pWins);
1,577,020✔
188
}
189

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

195
  SExtWinTrigGrpCtx* pCtx = (SExtWinTrigGrpCtx*)param;
1,431,273✔
196

197
  if (pCtx->pCGCtxs) {
1,431,273✔
198
    tSimpleHashSetFreeFp(pCtx->pCGCtxs, extWinDestroyCGrpCtx);
110,835✔
199
    tSimpleHashCleanup(pCtx->pCGCtxs);
110,835✔
200
  } else if (pCtx->pCCtx) {
1,320,438✔
201
    extWinDestroyCGrpCtx(pCtx->pCCtx);
1,317,010✔
202
    taosMemoryFree(pCtx->pCCtx);
1,317,010✔
203
  }
204
}
205

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

210
  if (listNEles(pExtW->pFreeBlocks) > 0) {
4,317,084✔
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,317,084✔
218
    TAOS_CHECK_EXIT(blockDataEnsureCapacity(pRes, TMAX(rows, 4096)));
4,317,084✔
219
    SArray* pIdx = taosArrayInit(10, sizeof(int64_t));
4,317,084✔
220
    TSDB_CHECK_NULL(pIdx, code, lino, _exit, terrno);
4,317,084✔
221
    void* res[2] = {pRes, pIdx};
4,317,084✔
222
    TAOS_CHECK_EXIT(tdListAppend(pList, res));
4,317,084✔
223

224
    *ppBlock = pRes;
4,317,084✔
225
    *ppIdx = pIdx;
4,317,084✔
226
    pExtW->stat.resBlockCreated++;
4,316,232✔
227
  }
228

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

233
  
234
_exit:
4,317,084✔
235

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

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

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

256
static void extWinAssignBlockGrpId(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW, SBlockID* pId) {
8,243,096✔
257
  uint64_t currentCGrpId = (pExtW->pTGrpCtx != NULL && pExtW->pTGrpCtx->pCCtx != NULL)
16,283,416✔
258
                               ? pExtW->pTGrpCtx->pCCtx->groupId
7,969,083✔
259
                               : pExtW->lastCGrpId;
16,283,416✔
260

261
  if (pExtW->calcWithPartition && !pOperator->pTaskInfo->pStreamRuntimeInfo->funcInfo.isMultiGroupCalc) {
8,243,096✔
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,492,395✔
267
    pId->baseGId = 0;
4,492,395✔
268
  } else if (pExtW->extWinSplit) {
3,750,701✔
269
    pId->groupId = currentCGrpId;
301,608✔
270
    pId->baseGId = pExtW->lastTGrpId;
301,608✔
271
  } else {
272
    pId->groupId = pExtW->lastTGrpId;
3,449,093✔
273
    pId->baseGId = 0;
3,449,093✔
274
  }
275

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

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

284
  pCCtx->blkWinIdx = -1;
3,936,724✔
285
  pCCtx->blkWinStartSet = false;
3,936,724✔
286
  pCCtx->blkRowStartIdx = 0;
3,936,724✔
287
}
288

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

293
  SListNode* pNode = TD_DLIST_TAIL(pList);
10,738,182✔
294
  if (NULL == pNode) {
10,738,182✔
295
    TAOS_CHECK_EXIT(extWinBlockListAddBlock(pExtW, pList, rows, ppBlock, ppIdx));
4,315,806✔
296
    return code;
4,315,806✔
297
  }
298

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

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

311
_exit:
6,421,098✔
312

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

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

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

342

343
static void extWinRecycleBlkNode(SExternalWindowOperator* pExtW, SListNode** ppNode) {
6,190,216✔
344
  if (NULL == ppNode || NULL == *ppNode) {
6,190,216✔
345
    return;
1,879,948✔
346
  }
347

348
  SSDataBlock* pBlock = *(SSDataBlock**)(*ppNode)->data;
4,310,268✔
349
  SArray* pIdx = *(SArray**)((SArray**)(*ppNode)->data + 1);
4,315,380✔
350
  
351
  if (listNEles(pExtW->pFreeBlocks) >= 10) {
4,315,380✔
352
    blockDataDestroy(pBlock);
4,251,480✔
353
    taosArrayDestroy(pIdx);
4,250,628✔
354
    taosMemoryFreeClear(*ppNode);
4,251,480✔
355
    pExtW->stat.resBlockDestroyed++;
4,251,480✔
356
    return;
4,251,480✔
357
  }
358
  
359
  blockDataCleanup(pBlock);
63,900✔
360
  taosArrayClear(pIdx);
63,900✔
361
  tdListPrependNode(pExtW->pFreeBlocks, *ppNode);
63,900✔
362
  *ppNode = NULL;
63,900✔
363
  pExtW->stat.resBlockRecycled++;
63,900✔
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,723,967✔
385
  if (NULL == pNode) {
1,723,967✔
386
    return;
1,659,215✔
387
  }
388

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

395
  taosMemoryFree(pNode);
64,752✔
396

397
  pInfo->stat.resBlockDestroyed++;
64,752✔
398
}
399

400

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

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

419
  extWinDestroyBlkNode(pInfo, pInfo->pLastBlkNode);
1,660,493✔
420
  if (pInfo->pFreeBlocks) {
1,660,067✔
421
    SListNode *node;
422
    while ((node = TD_DLIST_HEAD(pInfo->pFreeBlocks)) != NULL) {
120,558✔
423
      TD_DLIST_POP(pInfo->pFreeBlocks, node);
63,900✔
424
      extWinDestroyBlkNode(pInfo, node);
63,900✔
425
    }
426
    taosMemoryFree(pInfo->pFreeBlocks);
56,658✔
427
  }
428

429
  taosArrayDestroy(pInfo->pGrpIds);
1,660,067✔
430
  taosArrayDestroy(pInfo->pCTGrpIds);
1,660,493✔
431

432
  if (pInfo->ownTGrpCtx && pInfo->pTGrpCtx) {
1,660,067✔
433
    extWinDestroyTGrpCtx(pInfo->pTGrpCtx);
1,399,323✔
434
    taosMemoryFree(pInfo->pTGrpCtx);
1,399,323✔
435
    pInfo->pTGrpCtx = NULL;
1,399,323✔
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,660,067✔
443
    SStreamRuntimeFuncInfo* pRt = &pInfo->pTaskInfo->pStreamRuntimeInfo->funcInfo;
540,112✔
444
    if (pRt->pGroupCalcInfos != NULL) {
540,112✔
445
      int32_t iter = 0;
74,124✔
446
      void*   pIter = NULL;
74,124✔
447
      while ((pIter = tSimpleHashIterate(pRt->pGroupCalcInfos, pIter, &iter)) != NULL) {
221,094✔
448
        SSTriggerGroupCalcInfo* pGrpCalc = (SSTriggerGroupCalcInfo*)pIter;
146,970✔
449
        if (pGrpCalc->pRunnerGrpCtx != NULL) {
146,970✔
450
          extWinDestroyTGrpCtx(pGrpCalc->pRunnerGrpCtx);
31,950✔
451
          taosMemoryFree(pGrpCalc->pRunnerGrpCtx);
31,950✔
452
          pGrpCalc->pRunnerGrpCtx = NULL;
31,950✔
453
        }
454
      }
455
    }
456
  }
457

458
  cleanupAggSup(&pInfo->aggSup);
1,660,919✔
459
  cleanupExprSupp(&pInfo->scalarSupp);
1,660,067✔
460
  cleanupExprSupp(&pInfo->projSupp);
1,660,493✔
461
  for (int32_t i = 0; i < pInfo->resultRows.resRowsSize; ++i) {
17,649,001✔
462
    if (pInfo->resultRows.pResultRows && pInfo->resultRows.pResultRows[i]) {
15,988,508✔
463
      taosMemoryFreeClear(pInfo->resultRows.pResultRows[i]);
1,346,339✔
464
    }
465
  }
466
  taosMemoryFreeClear(pInfo->resultRows.pResultRows);
1,660,493✔
467
  
468
  pInfo->binfo.resultRowInfo.openWindow = tdListFree(pInfo->binfo.resultRowInfo.openWindow);
1,660,493✔
469

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

474
  taosMemoryFreeClear(pInfo);
1,660,493✔
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) {
929,420✔
483
    return;
×
484
  }
485

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

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

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

500
static void extWinApplyTimeRangeToExchangeParam(SOperatorParam* pParam, const STimeWindow* pTimeRange) {
28,542✔
501
  if (pParam == NULL || pParam->value == NULL || pTimeRange == NULL) {
28,542✔
502
    return;
28,542✔
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,
521,794✔
517
                                                           int32_t* pScanAppliedNum, SExecTaskInfo* pRootTaskInfo) {
518
  if (pOperator == NULL || pTimeRange == NULL || pScanAppliedNum == NULL || pRootTaskInfo == NULL) {
521,794✔
519
    return TSDB_CODE_INVALID_PARA;
×
520
  }
521

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

526
  if (pOperator->operatorType == QUERY_NODE_PHYSICAL_PLAN_TABLE_SCAN) {
521,794✔
527
    extWinApplyTimeRangeToTableScan(pOperator, pTimeRange);
464,710✔
528
    ++(*pScanAppliedNum);
464,710✔
529
  }
530

531
  if (pOperator->operatorType == QUERY_NODE_PHYSICAL_PLAN_EXCHANGE) {
521,794✔
532
    extWinApplyTimeRangeToExchangeParam(pOperator->pOperatorGetParam, pTimeRange);
28,542✔
533
  }
534

535
  if (pOperator->numOfDownstream <= 0 || pOperator->pDownstream == NULL) {
521,794✔
536
    return TSDB_CODE_SUCCESS;
493,252✔
537
  }
538

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

545
    if (pOperator->pDownstreamGetParams != NULL &&
45,156✔
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);
45,156✔
551
    if (code != TSDB_CODE_SUCCESS) {
45,156✔
552
      return code;
×
553
    }
554
  }
555

556
  return TSDB_CODE_SUCCESS;
28,542✔
557
}
558

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

564
  if (pTimeRange->skey == INT64_MAX || pTimeRange->ekey == INT64_MIN || pTimeRange->skey > pTimeRange->ekey) {
1,599,149✔
565
    return TSDB_CODE_SUCCESS;
1,122,511✔
566
  }
567

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

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

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

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

589
  return TSDB_CODE_SUCCESS;
476,638✔
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) {
60,492✔
604
  SMergeAlignedExternalWindowOperator* pMAExtW = (SMergeAlignedExternalWindowOperator*)pOperator;
60,492✔
605
  destroyExternalWindowOperatorInfo(pMAExtW->pExtW);
60,492✔
606
  taosMemoryFreeClear(pMAExtW);
60,492✔
607
}
60,492✔
608

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

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

619
    tsCols = (int64_t*)pColDataInfo->pData;
4,008,718✔
620
    if (pBlock->info.window.skey == 0 && pBlock->info.window.ekey == 0) {
4,008,718✔
621
      int32_t code = blockDataUpdateTsWindow(pBlock, primaryTsIndex);
3,637,748✔
622
      if (code != TSDB_CODE_SUCCESS) {
3,637,748✔
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,008,718✔
631
}
632

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

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

644
  return pOperator->info;
72,197,088✔
645
}
646

647
static FORCE_INLINE SExtWinCalcGrpCtx* extWinGetScopedCalcGrpCtx(SOperatorInfo* pOperator) {
648
  SExternalWindowOperator* pExtW = extWinGetCoreInfo(pOperator);
81,742,470✔
649
  if (pExtW == NULL || pExtW->pTGrpCtx == NULL || pExtW->pTGrpCtx->pCCtx == NULL) {
81,742,470✔
650
    return NULL;
181,050✔
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) {
81,560,994✔
657
    return pExtW->pTGrpCtx->pCCtx;
44,399,817✔
658
  }
659

660
  SExecTaskInfo* pTaskInfo = pOperator->pTaskInfo;
37,161,603✔
661
  if (pTaskInfo != NULL && pTaskInfo->pStreamRuntimeInfo != NULL) {
37,162,029✔
662
    SStreamRuntimeFuncInfo* pInfo = &pTaskInfo->pStreamRuntimeInfo->funcInfo;
37,162,029✔
663
    if (pInfo->isMultiGroupCalc) {
37,162,881✔
664
      return pExtW->pTGrpCtx->pCCtx;
×
665
    }
666
  }
667

668
  return NULL;
37,160,325✔
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);
45,819,106✔
677
  if (pCCtx != NULL) {
45,819,106✔
678
    return pCCtx->curIdx;
22,404,125✔
679
  }
680

681
  return pTaskInfo->pStreamRuntimeInfo->funcInfo.curIdx;
23,414,981✔
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);
31,598,186✔
688
    if (pCCtx != NULL) {
31,598,186✔
689
      pCCtx->curIdx = idx;
17,713,540✔
690
    }
691
    pTaskInfo->pStreamRuntimeInfo->funcInfo.curIdx = idx;
31,598,186✔
692
  }
693
}
2,147,483,647✔
694

695

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

701
  SExtWinCalcGrpCtx* pCCtx = extWinGetScopedCalcGrpCtx(pOperator);
4,314,954✔
702
  if (pCCtx != NULL) {
4,314,954✔
703
    pCCtx->curIdx++;
4,272,354✔
704
  }
705
  pOperator->pTaskInfo->pStreamRuntimeInfo->funcInfo.curIdx++;
4,314,954✔
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;
610,837,892✔
723
  *pRowIdx = pBlock->info.rows - rows;
610,840,874✔
724

725
  TSDB_CHECK_NULL(taosArrayPush(pIdx, &res), code, lino, _exit, terrno);
610,844,708✔
726

727
_exit:
610,844,708✔
728

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

733
  return code;
610,828,946✔
734
}
735

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

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

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

749
  int8_t* pIndicator = (int8_t*)pFilterRes->pData;
8,159✔
750
  int32_t newRowStart = 0;
8,159✔
751
  for (int32_t i = 0; i < idxSize; ++i) {
46,398✔
752
    int64_t cur = *(int64_t*)taosArrayGet(pIdx, i);
38,239✔
753
    int32_t* pCurWinIdx = (int32_t*)&cur;
38,239✔
754
    int32_t* pCurRowIdx = pCurWinIdx + 1;
38,239✔
755

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

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

771
    int32_t survivedRows = 0;
38,239✔
772
    for (int32_t r = startRow; r < endRow; ++r) {
76,478✔
773
      if (pIndicator[r]) {
38,239✔
774
        survivedRows++;
19,330✔
775
      }
776
    }
777

778
    if (survivedRows <= 0) {
38,239✔
779
      continue;
18,909✔
780
    }
781

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

789
    newRowStart += survivedRows;
19,330✔
790
  }
791

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

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

808

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

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

831
  if (pInfo->isMultiGroupCalc) {
457,949✔
832
    pInfo->pStreamPesudoFuncVals = pInfo->curGrpCalc->pParams;
31,950✔
833
    pInfo->pStreamPartColVals = pInfo->curGrpCalc->pGroupColVals;
31,950✔
834
  }
835

836
  size_t size = taosArrayGetSize(pInfo->pStreamPesudoFuncVals);
457,949✔
837
  pCtx->pWins = taosArrayInit_s(sizeof(SExtWinTimeWindow), size);
457,949✔
838
  TSDB_CHECK_NULL(pCtx->pWins, code, lino, _exit, terrno);
457,949✔
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);
457,949✔
843

844
  if (pExtW->isMergeAlignedExtW) {
457,949✔
845
    for (int32_t i = 0; i < size; ++i) {
2,454,186✔
846
      SSTriggerCalcParam* pParam = taosArrayGet(pInfo->pStreamPesudoFuncVals, i);
2,386,026✔
847

848
      pWin[i].tw.skey = pParam->wstart;
2,386,026✔
849
      pWin[i].tw.ekey = pParam->wstart + 1;
2,386,026✔
850
      pWin[i].resWinIdx = -1;
2,386,026✔
851
    }
852
  } else if (pExtW->timeRangeExpr && pExtW->timeRangeExpr->needCalc) {
389,789✔
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) {
22,641,233✔
865
      SSTriggerCalcParam* pParam = taosArrayGet(pInfo->pStreamPesudoFuncVals, i);
22,251,444✔
866

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

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

876
_exit:
389,789✔
877

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

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

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

909
  if ((!pInfo->isMultiGroupCalc && NULL != pExtW->pTGrpCtx) ||
458,300✔
910
      (pInfo->isMultiGroupCalc && NULL != pExtW->pTGrpCtx && pId->baseGId == pExtW->lastTGrpId)) {
298,921✔
911
    goto _exit;
161,083✔
912
  }
913
  
914
  if (pInfo->isMultiGroupCalc) {
297,217✔
915
    pInfo->curGrpCalc = tSimpleHashGet(pInfo->pGroupCalcInfos, &pId->baseGId, sizeof(pId->baseGId));
31,950✔
916
    if (NULL == pInfo->curGrpCalc) {
31,950✔
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) {
31,950✔
923
      pInfo->curGrpCalc->pRunnerGrpCtx = taosMemoryCalloc(1, sizeof(SExtWinTrigGrpCtx));
31,950✔
924
      TSDB_CHECK_NULL(pInfo->curGrpCalc->pRunnerGrpCtx, code, lino, _exit, terrno);
31,950✔
925
      
926
      if (!pExtW->calcWithPartition && pExtW->needGroupSort) {
31,950✔
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;
31,950✔
940
    pExtW->lastTGrpId = pId->baseGId;
31,950✔
941
    pExtW->pTGrpCtx = pInfo->curGrpCalc->pRunnerGrpCtx;
31,950✔
942
    pExtW->ownTGrpCtx = false;
31,950✔
943

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

947
    goto _exit;
31,950✔
948
  }
949

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

954
_exit:
458,300✔
955

956
  if (code) {
458,300✔
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;
458,300✔
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) {
538,814✔
967
    return false;
×
968
  }
969

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

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

978
  if (pId->baseGId != 0) {
37,914✔
979
    return;
34,932✔
980
  }
981

982
  if (pId->groupId != 0 &&
5,964✔
983
      tSimpleHashGet(pInfo->pGroupCalcInfos, &pId->groupId, sizeof(pId->groupId)) != NULL) {
2,982✔
984
    pId->baseGId = pId->groupId;
2,982✔
985
    qDebug("%s %s normalize baseGId <- groupId %" PRIu64,
2,982✔
986
           GET_TASKID(pTaskInfo), EXT_WIN_TYPE_STR(pExtW->isMergeAlignedExtW), pId->groupId);
987
    return;
2,982✔
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,
37,914✔
1016
                                                 SBlockID* pId) {
1017
  if (pId->groupId == 0 && pId->baseGId != 0) {
37,914✔
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
}
37,914✔
1023

1024
static void extWinResolveBlockIdForPartition(SExternalWindowOperator* pExtW, SExecTaskInfo* pTaskInfo, SBlockID* pId) {
538,814✔
1025
  if (!extWinNeedResolvePartitionBlockId(pExtW, pTaskInfo, pId)) {
538,814✔
1026
    return;
500,900✔
1027
  }
1028

1029
  extWinResolveBaseGroupIdForPartition(pExtW, pTaskInfo, pId);
37,914✔
1030
  extWinResolveCalcGroupIdForPartition(pExtW, pTaskInfo, pId);
37,914✔
1031
}
1032

1033

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

1039
  if (pTCtx == NULL) {
458,300✔
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))) {
458,300✔
1050
    if (NULL != pTCtx->pCGCtxs) {
197,458✔
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) {
197,458✔
1059
      pTCtx->pCCtx = taosMemoryCalloc(1, sizeof(*pTCtx->pCCtx));
186,382✔
1060
      TSDB_CHECK_NULL(pTCtx->pCCtx, code, lino, _exit, terrno);
186,382✔
1061
      TAOS_CHECK_EXIT(extWinInitCGrpCtx(pExtW, pTaskInfo, pTCtx->pCCtx));
186,382✔
1062
    }
1063

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

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

1077
  pExtW->lastCGrpId = pId->groupId;
256,582✔
1078
  
1079
  if (NULL == pTCtx->pCGCtxs) {
256,582✔
1080
    if (NULL != pTCtx->pCCtx) {
110,835✔
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));
110,835✔
1085
    TSDB_CHECK_NULL(pTCtx->pCGCtxs, code, lino, _exit, terrno);
110,835✔
1086
  }
1087

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

1095
    if (pExtW->needGroupSort) {
256,582✔
1096
      if (pInfo->isMultiGroupCalc) {
34,932✔
1097
        if (NULL == pExtW->pCTGrpIds) {
5,964✔
1098
          pExtW->pCTGrpIds = taosArrayInit(1024, sizeof(uint64_t) * 2);
4,686✔
1099
          TSDB_CHECK_NULL(pExtW->pCTGrpIds, code, lino, _exit, terrno);
4,686✔
1100
        }
1101
        
1102
        TSDB_CHECK_NULL(taosArrayPush(pExtW->pCTGrpIds, &pId->groupId), code, lino, _exit, terrno);
11,928✔
1103
      } else {
1104
        if (NULL == pExtW->pGrpIds) {
28,968✔
1105
          pExtW->pGrpIds = taosArrayInit(1024, sizeof(uint64_t));
21,300✔
1106
          TSDB_CHECK_NULL(pExtW->pGrpIds, code, lino, _exit, terrno);
21,300✔
1107
        }
1108
        
1109
        TSDB_CHECK_NULL(taosArrayPush(pExtW->pGrpIds, &pId->groupId), code, lino, _exit, terrno);
57,936✔
1110
      }
1111
    }
1112
  }
1113
  if (pTCtx->pCCtx != NULL) {
256,582✔
1114
    pTCtx->pCCtx->groupId = pId->groupId;
256,582✔
1115
  }
1116
  
1117
  qDebug("%s ext win switch to cgrp %" PRIu64, EXT_WIN_TYPE_STR(pExtW->isMergeAlignedExtW), pId->groupId);
256,582✔
1118

1119
_exit:
×
1120

1121
  if (code) {
458,300✔
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;
458,300✔
1127
}
1128

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

1132
  if (pTaskInfo->pStreamRuntimeInfo == NULL) {
4,008,718✔
1133
    return TSDB_CODE_SUCCESS;
3,550,418✔
1134
  }
1135

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

1140
_exit:
458,300✔
1141

1142
  if (code) {
458,300✔
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;
458,300✔
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,368,134✔
1157
                                       SExprSupp* pExprSup, SAggSupporter* pAggSup) {
1158
  if (*pResult == NULL) {
2,368,134✔
1159
    *pResult = getNewResultRow(pAggSup->pResultBuf, &pAggSup->currentPageId, pAggSup->resultRowSize);
59,640✔
1160
    if (!*pResult) {
59,640✔
1161
      qError("get new resultRow failed, err:%s", tstrerror(terrno));
×
1162
      return terrno;
×
1163
    }
1164
    pResultRowInfo->cur = (SResultRowPosition){.pageId = (*pResult)->pageId, .offset = (*pResult)->offset};
59,640✔
1165
  }
1166
  
1167
  (*pResult)->win = *pWin;
2,368,134✔
1168
  (*pResult)->winIdx = extWinGetCurWinIdx(pOperator);
2,368,134✔
1169
  
1170
  return setResultRowInitCtx((*pResult), pExprSup->pCtx, pExprSup->numOfExprs, pExprSup->rowEntryInfoOffset);
2,368,134✔
1171
}
1172

1173

1174
static int32_t mergeAlignExtWinGetWinFromTs(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW, TSKEY ts, STimeWindow** ppWin) {
2,368,134✔
1175
  int32_t blkWinIdx = extWinGetCurWinIdx(pOperator);
2,368,134✔
1176
  
1177
  // TODO handle desc order
1178
  for (int32_t i = blkWinIdx; i < pExtW->pTGrpCtx->pCCtx->pWins->size; ++i) {
14,486,130✔
1179
    STimeWindow* pWin = taosArrayGet(pExtW->pTGrpCtx->pCCtx->pWins, i);
14,486,130✔
1180
    if (ts == pWin->skey) {
14,486,130✔
1181
      extWinSetCurWinIdx(pOperator, i);
2,368,134✔
1182
      *ppWin = pWin;
2,368,134✔
1183
      return TSDB_CODE_SUCCESS;
2,368,134✔
1184
    } else if (ts < pWin->skey) {
12,117,996✔
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,368,134✔
1196
  int32_t        code = 0, lino = 0;
2,368,134✔
1197
  SMergeAlignedExternalWindowOperator* pMAExtW = pOperator->info;
2,368,134✔
1198
  SExternalWindowOperator*             pExtW = pMAExtW->pExtW;
2,368,134✔
1199
  SExprSupp*     pSup = &pOperator->exprSupp;
2,368,134✔
1200
  SResultRow*  pResultRow = pMAExtW->pResultRow;
2,368,134✔
1201
  
1202
  finalizeResultRows(pExtW->aggSup.pResultBuf, &pResultRowInfo->cur, pSup, pResultBlock, pOperator->pTaskInfo);
2,368,134✔
1203

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

1208
_exit:
2,368,134✔
1209

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

1214
  return code;
2,368,134✔
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) {
71,994✔
1245
  SMergeAlignedExternalWindowOperator* pMAExtW = pOperator->info;
71,994✔
1246
  SExternalWindowOperator*             pExtW = pMAExtW->pExtW;
71,994✔
1247

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

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

1257
  extWinSetCurWinIdx(pOperator, 0);
71,994✔
1258
  code = mergeAlignExtWinGetWinFromTs(pOperator, pExtW, ts, &pWin);
71,994✔
1259
  if (code) {
71,994✔
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);
71,994✔
1266

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

1273
  if (pExtW->isDynWindow && pMAExtW->lastFinalizedWinIdx + 1 < newWinIdx) {
71,994✔
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));
71,994✔
1279

1280
  int32_t currPos = startPos;
71,994✔
1281
  pMAExtW->curTs = pWin->skey;
71,994✔
1282
  
1283
  while (++currPos < pBlock->info.rows) {
4,602,078✔
1284
    if (tsCols[currPos] == pMAExtW->curTs) continue;
4,530,084✔
1285

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

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

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

1298
    if (pExtW->isDynWindow && pMAExtW->lastFinalizedWinIdx + 1 < newWinIdx) {
2,296,140✔
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,296,140✔
1304
    startPos = currPos;
2,296,140✔
1305
    
1306
    TAOS_CHECK_EXIT(mergeAlignExtWinSetOutputBuf(pOperator, pResultRowInfo, pWin, &pMAExtW->pResultRow, pSup, &pExtW->aggSup));
2,296,140✔
1307

1308
    pMAExtW->curTs = pWin->skey;
2,296,140✔
1309
  }
1310

1311
  code = applyAggFunctionOnPartialTuples(pTaskInfo, pSup->pCtx, &pExtW->twAggSup.timeWindowData, startPos,
143,988✔
1312
                                         currPos - startPos, pBlock->info.rows, pSup->numOfExprs);
71,994✔
1313

1314
_exit:
71,994✔
1315

1316
  if (code != 0) {
71,994✔
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;
71,994✔
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) {
69,438✔
1372
  SExecTaskInfo*                       pTaskInfo = pOperator->pTaskInfo;
69,438✔
1373
  SMergeAlignedExternalWindowOperator* pMAExtW = pOperator->info;
69,438✔
1374
  SExternalWindowOperator*             pExtW = pMAExtW->pExtW;
69,438✔
1375
  SResultRow*                          pResultRow = NULL;
69,438✔
1376
  int32_t                              code = 0;
69,438✔
1377
  SSDataBlock*                         pRes = pExtW->binfo.pRes;
69,438✔
1378
  SExprSupp*                           pSup = &pOperator->exprSupp;
69,438✔
1379
  int32_t                              lino = 0;
69,438✔
1380
  SStreamRuntimeFuncInfo*              pStream = &pOperator->pTaskInfo->pStreamRuntimeInfo->funcInfo;
69,438✔
1381

1382
  taosArrayClear(pExtW->pWinRowIdx);
69,438✔
1383
  blockDataCleanup(pRes);
69,438✔
1384

1385
  SSDataBlock* pBlock = NULL;
69,438✔
1386
  while (1) {
1387
    if (pMAExtW->pNewGroup != NULL) {
141,006✔
1388
      pBlock = pMAExtW->pNewGroup;
8,520✔
1389
      pMAExtW->pNewGroup = NULL;
8,520✔
1390
    } else {
1391
      pBlock = getNextBlockFromDownstream(pOperator, 0);
132,486✔
1392
    }
1393

1394
    if (pBlock == NULL) {
141,006✔
1395
      // close last time window
1396
      if (pMAExtW->curTs != INT64_MIN && EEXT_MODE_AGG == pExtW->mode) {
60,492✔
1397
        TAOS_CHECK_EXIT(mergeAlignExtWinFinalizeResult(pOperator, &pExtW->binfo.resultRowInfo, pRes));
59,640✔
1398
        pMAExtW->lastFinalizedWinIdx = pMAExtW->pResultRow->winIdx;
59,640✔
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) {
60,492✔
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);
60,492✔
1408
      break;
60,492✔
1409
    }
1410

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

1413
    if (pExtW->lastCGrpId != pBlock->info.id.groupId) {
80,514✔
1414
      if (pMAExtW->curTs != INT64_MIN && EEXT_MODE_AGG == pExtW->mode) {
25,986✔
1415
        TAOS_CHECK_EXIT(mergeAlignExtWinFinalizeResult(pOperator, &pExtW->binfo.resultRowInfo, pRes));
8,520✔
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,520✔
1421
          resetResultRow(pMAExtW->pResultRow, pExtW->aggSup.resultRowSize - sizeof(SResultRow));
8,520✔
1422
        }
1423

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

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

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

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

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

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

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

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

1455
  pStream->pStreamBlkWinIdx = pExtW->pWinRowIdx;
69,438✔
1456

1457
  extWinPostUpdateStreamRt(pStream, pOperator, pExtW);
69,438✔
1458
  
1459
_exit:
69,438✔
1460

1461
  if (code != 0) {
69,438✔
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
}
69,438✔
1467

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

1475
  if (pOperator->status == OP_EXEC_DONE) {
127,800✔
1476
    (*ppRes) = NULL;
58,362✔
1477
    return TSDB_CODE_SUCCESS;
58,362✔
1478
  }
1479

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

1483
  mergeAlignExtWinDo(pOperator);
69,438✔
1484

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

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

1496
_exit:
69,438✔
1497

1498
  if (code != 0) {
69,438✔
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;
69,438✔
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,602,983✔
1554
  if (pFillExprs == NULL) {
1,602,983✔
1555
    return TSDB_CODE_SUCCESS;
1,583,207✔
1556
  }
1557

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

1565
  SNode* pFNode = NULL;
19,776✔
1566
  FOREACH(pFNode, pFillExprs) {
49,415✔
1567
    int16_t   dstSlot = -1;
29,639✔
1568
    SDataType dtype = {0};
29,639✔
1569
    int16_t   colId = 0;
29,639✔
1570
    bool      isPk = false;
29,639✔
1571
    bool      found = false;
29,639✔
1572

1573
    if (nodeType(pFNode) == QUERY_NODE_TARGET) {
29,639✔
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) {
29,639✔
1587
      SColumnNode* pCol = (SColumnNode*)pFNode;
29,639✔
1588
      dstSlot = pCol->slotId;
29,639✔
1589
      dtype = pCol->node.resType;
29,639✔
1590
      colId = pCol->colId;
29,639✔
1591
      isPk = pCol->isPk;
29,639✔
1592
      found = true;
29,639✔
1593
    }
1594

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

1609
  return TSDB_CODE_SUCCESS;
19,776✔
1610
}
1611

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1701
  code = appendDownstream(pOperator, &pDownstream, 1);
60,492✔
1702
  QUERY_CHECK_CODE(code, lino, _error);
60,492✔
1703
  *ppOptrOut = pOperator;
60,492✔
1704
  return code;
60,492✔
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,586✔
1716
  if (pNodeList == NULL) {
2,586✔
1717
    return TSDB_CODE_SUCCESS;
×
1718
  }
1719

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

1727
  return initExprSupp(pExprSupp, pExprInfo, num, &pTaskInfo->storageAPI.functionStore);
2,586✔
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) {
7,938,950✔
1817
  if (QUERY_NODE_FUNCTION == nodeType(pNode)) {
7,938,950✔
1818
    SFunctionNode* pFunc = (SFunctionNode*)pNode;
2,904,145✔
1819
    if (fmIsCountLikeFunc(pFunc->funcId) || (pFunc->hasOriginalFunc && fmIsCountLikeFunc(pFunc->originalFuncId))) {
2,904,145✔
1820
      *(bool*)res = true;
934,461✔
1821
      return DEAL_RES_END;
936,591✔
1822
    }
1823
  }
1824
  return DEAL_RES_CONTINUE;
7,004,063✔
1825
}
1826

1827

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

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

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

1842
  SExprSupp* pSupps[] = {&pOperator->exprSupp, &pExtW->scalarSupp};
47,641✔
1843
  for (int32_t s = 0; s < 2; ++s) {
142,923✔
1844
    SExprSupp* pSupp = pSupps[s];
95,282✔
1845
    if (pSupp == NULL || pSupp->pExprInfo == NULL) {
95,282✔
1846
      continue;
47,641✔
1847
    }
1848

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

1855
      SExprInfo* pOneExpr = &pSupp->pExprInfo[i];
137,691✔
1856
      for (int32_t j = 0; j < pOneExpr->base.numOfParams; ++j) {
267,373✔
1857
        SFunctParam* pFuncParam = &pOneExpr->base.pParam[j];
129,682✔
1858
        if (pFuncParam->type != FUNC_PARAM_TYPE_COLUMN) {
129,682✔
1859
          continue;
62,726✔
1860
        }
1861

1862
        int32_t slotId = pFuncParam->pCol->slotId;
66,956✔
1863
        int32_t numOfCols = taosArrayGetSize(pBlock->pDataBlock);
66,956✔
1864
        if (slotId < numOfCols) {
66,956✔
1865
          continue;
2,586✔
1866
        }
1867

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

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

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

1882
  for (int32_t i = 0; i < blockDataGetNumOfCols(pBlock); ++i) {
140,026✔
1883
    SColumnInfoData* pColInfoData = taosArrayGet(pBlock->pDataBlock, i);
92,385✔
1884
    QUERY_CHECK_NULL(pColInfoData, code, lino, _end, terrno);
92,385✔
1885
    colDataSetNULL(pColInfoData, 0);
1886
  }
1887
  *ppBlock = pBlock;
47,641✔
1888

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

1897

1898

1899
static int extWinTsWinCompare(const void* pLeft, const void* pRight) {
18,614,202✔
1900
  int64_t ts = *(int64_t*)pLeft;
18,614,202✔
1901
  SExtWinTimeWindow* pWin = (SExtWinTimeWindow*)pRight;
18,614,202✔
1902
  if (ts < pWin->tw.skey) {
18,614,202✔
1903
    return -1;
11,468,986✔
1904
  }
1905
  if (ts >= pWin->tw.ekey) {
7,145,216✔
1906
    return 1;
5,367,962✔
1907
  }
1908

1909
  return 0;
1,777,254✔
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;
1,980,952✔
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", 
868,797✔
2034
        GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, (int32_t)pCCtx->pWins->size);
2035
    *ppWin = NULL;
868,797✔
2036
    return TSDB_CODE_SUCCESS;
868,797✔
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", 
979,852✔
2042
        GET_TASKID(pOperator->pTaskInfo), __func__, tsCol[pInfo->rows - 1], pCCtx->blkWinIdx, pWin->tw.skey);
2043
    *ppWin = NULL;
979,852✔
2044
    return TSDB_CODE_SUCCESS;
979,852✔
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;
321,697,435✔
2078
    }
2079

2080
    if (r >= pInfo->rows) {
321,804,787✔
2081
      if (!pCCtx->blkWinStartSet) {
107,352✔
2082
        pCCtx->blkWinStartIdx = pCCtx->blkWinIdx;
107,352✔
2083
      }
2084
      
2085
      break;
107,352✔
2086
    }
2087
  }
2088

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

2092
  *ppWin = NULL;
132,303✔
2093
  return TSDB_CODE_SUCCESS;
132,303✔
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,
1,955,772✔
2196
                                       int64_t rowNum, int32_t* startPos) {
2197
  SExtWinTimeWindow* pWin = NULL;
1,955,772✔
2198
  int32_t            idx = taosArraySearchIdx(pWins, tsCol, extWinTsWinCompare, TD_EQ);
1,955,772✔
2199
  if (idx >= 0) {
1,955,772✔
2200
    for (int i = idx - 1; i >= 0; --i) {
1,777,254✔
2201
      pWin = TARRAY_GET_ELEM(pWins, i);
840,929✔
2202
      if (extWinTsWinCompare(tsCol, pWin) == 0) {
840,929✔
2203
        idx = i;
2,130✔
2204
      } else {
2205
        break;
838,799✔
2206
      }
2207
    }
2208
    *startPos = 0;
1,775,124✔
2209
    return idx;
1,775,124✔
2210
  }
2211

2212
  pWin = NULL;
180,648✔
2213
  int32_t w = 0;
180,648✔
2214
  for (int64_t i = 1; i < rowNum; ++i) {
62,732,658✔
2215
    for (; w < pWins->size; ++w) {
338,602,246✔
2216
      pWin = TARRAY_GET_ELEM(pWins, w);
338,598,838✔
2217
      if (tsCol[i] < pWin->tw.skey) {
338,598,838✔
2218
        break;
62,548,602✔
2219
      }
2220

2221
      if (tsCol[i] < pWin->tw.ekey) {
276,050,236✔
2222
        *startPos = i;
156,396✔
2223
        return w;
156,396✔
2224
      }
2225
    }
2226
  }
2227

2228
  return -1;
24,252✔
2229
}
2230

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

2235
  if (pCCtx->blkWinIdx < 0) {
1,881,338,999✔
2236
    pCCtx->blkWinIdx = extWinGetFirstWinFromTs(pOperator, pCCtx->pWins, tsCol, pInfo->rows, startPos);
1,955,772✔
2237
    if (pCCtx->blkWinIdx < 0) {
1,955,772✔
2238
      qDebug("%s %s blk TR[%" PRId64 ", %" PRId64 ") not in any win, skip block", 
24,252✔
2239
          GET_TASKID(pOperator->pTaskInfo), __func__, tsCol[0], tsCol[pInfo->rows - 1]);
2240
      *ppWin = NULL;
24,252✔
2241
      return TSDB_CODE_SUCCESS;
24,252✔
2242
    }
2243

2244
    extWinSetCurWinIdx(pOperator, pCCtx->blkWinIdx);
1,931,520✔
2245
    *ppWin = taosArrayGet(pCCtx->pWins, pCCtx->blkWinIdx);
1,931,520✔
2246
    *winRows = getNumOfRowsInTimeWindow(pInfo, tsCol, *startPos, (*ppWin)->tw.ekey - 1, binarySearchForKey, NULL, pExtW->binfo.inputTsOrder);
1,931,520✔
2247
    
2248
    qDebug("%s %s the %dth ext win TR[%" PRId64 ", %" PRId64 ") got %d rows rowStartidx %d ts[%" PRId64 ", %" PRId64 "] in blk", 
1,931,520✔
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;
1,931,520✔
2252
  } else {
2253
    pCCtx->blkWinIdx++;
1,879,383,227✔
2254
  }
2255

2256
  if (pCCtx->blkWinIdx >= pCCtx->pWins->size) {
1,879,382,801✔
2257
    qDebug("%s %s ext win blk idx %d reach the end, size: %d, skip block", 
807,729✔
2258
        GET_TASKID(pOperator->pTaskInfo), __func__, pCCtx->blkWinIdx, (int32_t)pCCtx->pWins->size);
2259
    *ppWin = NULL;
807,729✔
2260
    return TSDB_CODE_SUCCESS;
807,729✔
2261
  }
2262
  
2263
  SExtWinTimeWindow* pWin = taosArrayGet(pCCtx->pWins, pCCtx->blkWinIdx);
1,878,575,498✔
2264
  if (tsCol[pInfo->rows - 1] < pWin->tw.skey) {
1,878,575,498✔
2265
    qDebug("%s %s block end ts %" PRId64 " is small than curr win %d skey %" PRId64 ", skip block", 
1,106,955✔
2266
        GET_TASKID(pOperator->pTaskInfo), __func__, tsCol[pInfo->rows - 1], pCCtx->blkWinIdx, pWin->tw.skey);
2267
    *ppWin = NULL;
1,106,955✔
2268
    return TSDB_CODE_SUCCESS;
1,106,955✔
2269
  }
2270

2271
  int64_t r = 0;
1,877,468,543✔
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,877,468,543✔
2274

2275
  // TODO handle desc order
2276
  for (; pCCtx->blkWinIdx < pCCtx->pWins->size; ++pCCtx->blkWinIdx) {
2,082,545,567✔
2277
    pWin = taosArrayGet(pCCtx->pWins, pCCtx->blkWinIdx);
2,082,530,564✔
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,082,528,731✔
2285
        extWinSetCurWinIdx(pOperator, pCCtx->blkWinIdx);
1,877,451,707✔
2286
        *ppWin = pWin;
1,877,451,707✔
2287
        *startPos = r;
1,877,451,707✔
2288
        *winRows = getNumOfRowsInTimeWindow(pInfo, tsCol, r, pWin->tw.ekey - 1, binarySearchForKey, NULL, pExtW->binfo.inputTsOrder);
1,877,451,707✔
2289

2290
        qDebug("%s %s the %dth ext win TR[%" PRId64 ", %" PRId64 ") got %d rows rowStartidx %d ts[%" PRId64 ", %" PRId64 "] in blk", 
1,877,451,707✔
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,877,451,707✔
2294
      }
2295

2296
      break;
205,077,024✔
2297
    }
2298

2299
    if (r >= pInfo->rows) {
205,079,283✔
2300
      break;
2,259✔
2301
    }
2302
  }
2303

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

2307
  *ppWin = NULL;
17,262✔
2308
  return TSDB_CODE_SUCCESS;
16,836✔
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,371,402✔
2321
      pRows->pResultRows[pRows->resRowsIdx] = taosMemoryMalloc(pRows->resRowSize * resultRowSize);
1,346,339✔
2322
      TSDB_CHECK_NULL(pRows->pResultRows[pRows->resRowsIdx], code, lino, _exit, terrno);
1,346,339✔
2323
      pRows->resRowAllcNum += pRows->resRowSize;
1,346,339✔
2324
      continue;
1,346,339✔
2325
    }
2326

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

2332
    if (pRows->resRowsIdx >= pRows->resRowsSize) {
25,063✔
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;
179,137✔
2368
}
2369

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

2375
  for (int32_t i = 0; i < taosArrayGetSize(pCCtx->pWins); ++i) {
147,674✔
2376
    SExtWinTimeWindow* pWin = TARRAY_GET_ELEM(pCCtx->pWins, i);
139,996✔
2377
    if (pWin->resWinIdx < 0) {
139,996✔
2378
      continue;
69,933✔
2379
    }
2380

2381
    SResultRow* pRow = extWinGetResultRowByIdx(pExtW, pWin->resWinIdx, pExtW->aggSup.resultRowSize);
140,126✔
2382
    if (extWinRowHasSourceData(pRow)) {
70,063✔
2383
      return true;
70,063✔
2384
    }
2385
  }
2386

2387
  return false;
7,678✔
2388
}
2389

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

2395
  switch (pExtW->fillMode) {
88,085✔
2396
    case FILL_MODE_NONE:
×
2397
      return false;
×
2398
    case FILL_MODE_NULL_F:
10,344✔
2399
    case FILL_MODE_VALUE_F:
2400
      return true;
10,344✔
2401
    case FILL_MODE_PREV:
77,741✔
2402
    case FILL_MODE_NEXT:
2403
    case FILL_MODE_NULL:
2404
    case FILL_MODE_VALUE:
2405
      return extWinHasNaturalRows(pExtW, pCCtx);
77,741✔
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;
90,510✔
2413
}
2414

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

2421
  for (int32_t i = start; i != end; i += step) {
36,635✔
2422
    SExtWinTimeWindow* pWin = TARRAY_GET_ELEM(pCCtx->pWins, i);
26,722✔
2423
    if (pWin->resWinIdx < 0) {
26,722✔
2424
      continue;
12,930✔
2425
    }
2426

2427
    SResultRow* pRow = extWinGetResultRowByIdx(pExtW, pWin->resWinIdx, pExtW->aggSup.resultRowSize);
27,584✔
2428
    if (extWinRowHasSourceData(pRow)) {
13,792✔
2429
      return pRow;
13,792✔
2430
    }
2431
  }
2432

2433
  return NULL;
9,913✔
2434
}
2435

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

2442
  SNodeListNode* pValueList = (SNodeListNode*)pExtW->pFillValues;
46,979✔
2443
  int32_t matchNum = taosArrayGetSize(pExtW->fillMatchInfo.pList);
46,979✔
2444
  for (int32_t i = 0; i < matchNum; ++i) {
105,595✔
2445
    SColMatchItem* pItem = taosArrayGet(pExtW->fillMatchInfo.pList, i);
58,616✔
2446
    if (pItem == NULL || !pItem->needOutput || pItem->dstSlotId < 0) {
58,616✔
2447
      continue;
×
2448
    }
2449

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

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

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

2469
  return TSDB_CODE_SUCCESS;
46,979✔
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;
7,758✔
2479
  int32_t lino = 0;
7,758✔
2480

2481
  if (startRow < 0 || startRow + numOfRows > pBlock->info.rows) {
7,758✔
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) {
7,758✔
2488
    TAOS_CHECK_EXIT(createOneDataBlock(pBlock, false, &pExtW->pProjTmpBlock));
2,586✔
2489
  }
2490
  blockDataCleanup(pExtW->pProjTmpBlock);
7,758✔
2491
  TAOS_CHECK_EXIT(blockDataEnsureCapacity(pExtW->pProjTmpBlock, numOfRows));
7,758✔
2492
  TAOS_CHECK_EXIT(blockDataMergeNRows(pExtW->pProjTmpBlock, pBlock, startRow, numOfRows));
7,758✔
2493

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

2499
  int32_t numOfCols = taosArrayGetSize(pBlock->pDataBlock);
7,758✔
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) {
46,548✔
2504
    SColumnInfoData* pDstCol = taosArrayGet(pBlock->pDataBlock, i);
38,790✔
2505
    SColumnInfoData* pSrcCol = taosArrayGet(pSlice->pDataBlock, i);
38,790✔
2506
    if (pDstCol == NULL || pSrcCol == NULL) {
38,790✔
2507
      continue;
×
2508
    }
2509

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

2513
_exit:
7,758✔
2514
  return code;
7,758✔
2515
}
2516

2517
static int32_t extWinAppendAggFilledRow(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW,
90,510✔
2518
                                        SExtWinCalcGrpCtx* pCCtx, SExtWinTimeWindow* pWin, SResultRow* pSrcRow) {
2519
  int32_t         code = TSDB_CODE_SUCCESS;
90,510✔
2520
  int32_t         lino = 0;
90,510✔
2521
  SSDataBlock*    pBlock = pExtW->binfo.pRes;
90,510✔
2522
  SExecTaskInfo*  pTaskInfo = pOperator->pTaskInfo;
90,510✔
2523
  SExprInfo*      pExprInfo = pOperator->exprSupp.pExprInfo;
90,510✔
2524
  int32_t         numOfExprs = pOperator->exprSupp.numOfExprs;
90,510✔
2525
  int32_t*        rowEntryOffset = pOperator->exprSupp.rowEntryInfoOffset;
90,510✔
2526
  SqlFunctionCtx* pCtx = pOperator->exprSupp.pCtx;
90,510✔
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) {
90,510✔
2533
    int32_t startRow = pBlock->info.rows;
76,718✔
2534
    if (pBlock->info.rows + 1 > pBlock->info.capacity) {
76,718✔
2535
      TAOS_CHECK_EXIT(blockDataEnsureCapacity(pBlock, pBlock->info.rows + 1));
30,170✔
2536
    }
2537
    for (int32_t j = 0; j < taosArrayGetSize(pBlock->pDataBlock); ++j) {
368,074✔
2538
      SColumnInfoData* pColInfo = taosArrayGet(pBlock->pDataBlock, j);
291,356✔
2539
      if (pColInfo != NULL) {
291,356✔
2540
        colDataSetNULL(pColInfo, pBlock->info.rows);
291,356✔
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;
76,718✔
2547
    int32_t     groupKeyIdx = 0;
76,718✔
2548
    for (int32_t j = 0; j < numOfExprs; ++j) {
285,322✔
2549
      int32_t slotId = pExprInfo[j].base.resSchema.slotId;
208,604✔
2550
      SColumnInfoData* pColInfo = taosArrayGet(pBlock->pDataBlock, slotId);
208,604✔
2551
      if (pColInfo == NULL) continue;
208,604✔
2552

2553
      if (pCtx[j].isPseudoFunc) {
208,604✔
2554
        int32_t funcType = pCtx[j].pExpr->pExpr->_function.functionType;
98,699✔
2555
        if (funcType == FUNCTION_TYPE_WSTART || funcType == FUNCTION_TYPE_TWSTART) {
98,699✔
2556
          TAOS_CHECK_EXIT(colDataSetVal(pColInfo, pBlock->info.rows, (const char*)&pWin->tw.skey, false));
75,856✔
2557
        } else if (funcType == FUNCTION_TYPE_WEND || funcType == FUNCTION_TYPE_TWEND) {
22,843✔
2558
          TAOS_CHECK_EXIT(colDataSetVal(pColInfo, pBlock->info.rows, (const char*)&pWin->tw.ekey, false));
3,448✔
2559
        } else if (funcType == FUNCTION_TYPE_WDURATION || funcType == FUNCTION_TYPE_TWDURATION) {
19,395✔
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) {
19,395✔
2563
          // w.mark is window metadata — always show the current window's own value.
2564
          SStreamRuntimeFuncInfo* pStreamInfo = &pTaskInfo->pStreamRuntimeInfo->funcInfo;
19,395✔
2565
          SNodeList* pParamList = pCtx[j].pExpr->base.pParamList;
19,395✔
2566
          if (pParamList != NULL && LIST_LENGTH(pParamList) >= 2) {
19,395✔
2567
            SNode* pSecond = nodesListGetNode(pParamList, 1);
19,395✔
2568
            if (pSecond != NULL && nodeType(pSecond) == QUERY_NODE_VALUE) {
19,395✔
2569
              int32_t placeholderNo = ((SValueNode*)pSecond)->placeholderNo;
19,395✔
2570
              SSTriggerCalcParam* pCurParam = taosArrayGet(pStreamInfo->pStreamPesudoFuncVals, pCCtx->outWinIdx);
19,395✔
2571
              if (pCurParam != NULL && pCurParam->pExternalWindowData != NULL &&
19,395✔
2572
                  placeholderNo >= 0 && placeholderNo < (int32_t)taosArrayGetSize(pCurParam->pExternalWindowData)) {
19,395✔
2573
                SStreamGroupValue* pVal = taosArrayGet(pCurParam->pExternalWindowData, placeholderNo);
19,395✔
2574
                if (pVal != NULL) {
19,395✔
2575
                  if (pVal->isNull) {
19,395✔
2576
                    colDataSetNULL(pColInfo, pBlock->info.rows);
×
2577
                  } else if (IS_VAR_DATA_TYPE(pVal->data.type) || pVal->data.type == TSDB_DATA_TYPE_DECIMAL) {
19,395✔
2578
                    TAOS_CHECK_EXIT(colDataSetVal(pColInfo, pBlock->info.rows, (const char*)pVal->data.pData, false));
2,586✔
2579
                  } else {
2580
                    TAOS_CHECK_EXIT(colDataSetVal(pColInfo, pBlock->info.rows, (const char*)&pVal->data.val, false));
16,809✔
2581
                  }
2582
                }
2583
              }
2584
            }
2585
          }
2586
        }
2587
      } else if (fmIsGroupKeyFunc(pCtx[j].functionId) || fmisSelectGroupConstValueFunc(pCtx[j].functionId)) {
109,905✔
2588
        bool patched = false;
21,981✔
2589

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

2597
          if (pVals != NULL && groupKeyIdx < taosArrayGetSize(pVals)) {
21,981✔
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;
21,981✔
2612
        }
2613

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

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

2642
    pBlock->info.rows += 1;
76,718✔
2643
    TAOS_CHECK_EXIT(extWinApplyAggPostProjection(pOperator, pExtW, pBlock, startRow, 1));
76,718✔
2644
    if (extWinUsesValueFillMode(pExtW->fillMode)) {
153,436✔
2645
      TAOS_CHECK_EXIT(extWinApplyValueFill(pExtW, pBlock, startRow, 1));
46,979✔
2646
    }
2647
    TAOS_CHECK_EXIT(extWinAppendWinIdx(pTaskInfo, pExtW->pWinRowIdx, pBlock, pCCtx->outWinIdx, 1));
76,718✔
2648
    return code;
76,718✔
2649
  }
2650

2651
  char* pTmpBuf = pExtW->pFillRowBuf;
13,792✔
2652
  if (pTmpBuf == NULL) {
13,792✔
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);
13,792✔
2657
  SResultRow* pTmpRow = (SResultRow*)pTmpBuf;
13,792✔
2658
  pTmpRow->win = pWin->tw;
13,792✔
2659
  pTmpRow->winIdx = pCCtx->outWinIdx;
13,792✔
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);
13,792✔
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) {
49,134✔
2668
    if (!pCtx[j].isPseudoFunc) continue;
35,342✔
2669
    int32_t funcType = pCtx[j].pExpr->pExpr->_function.functionType;
15,947✔
2670
    if (funcType == FUNCTION_TYPE_WSTART || funcType == FUNCTION_TYPE_TWSTART) {
15,947✔
2671
      SResultRowEntryInfo* pEntryInfo = getResultEntryInfo(pTmpRow, j, rowEntryOffset);
13,792✔
2672
      *(int64_t*)GET_ROWCELL_INTERBUF(pEntryInfo) = pWin->tw.skey;
13,792✔
2673
    } else if (funcType == FUNCTION_TYPE_WEND || funcType == FUNCTION_TYPE_TWEND) {
2,155✔
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,155✔
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,155✔
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,155✔
2683
      SNodeList* pParamList = pCtx[j].pExpr->base.pParamList;
2,155✔
2684
      if (pParamList != NULL && LIST_LENGTH(pParamList) >= 2) {
2,155✔
2685
        SNode* pSecond = nodesListGetNode(pParamList, 1);
2,155✔
2686
        if (pSecond != NULL && nodeType(pSecond) == QUERY_NODE_VALUE) {
2,155✔
2687
          int32_t placeholderNo = ((SValueNode*)pSecond)->placeholderNo;
2,155✔
2688
          SSTriggerCalcParam* pCurParam = taosArrayGet(pStreamInfo->pStreamPesudoFuncVals, pCCtx->outWinIdx);
2,155✔
2689
          if (pCurParam != NULL && pCurParam->pExternalWindowData != NULL &&
2,155✔
2690
              placeholderNo >= 0 && placeholderNo < (int32_t)taosArrayGetSize(pCurParam->pExternalWindowData)) {
2,155✔
2691
            SStreamGroupValue* pVal = taosArrayGet(pCurParam->pExternalWindowData, placeholderNo);
2,155✔
2692
            if (pVal != NULL) {
2,155✔
2693
              SResultRowEntryInfo* pEntryInfo = getResultEntryInfo(pTmpRow, j, rowEntryOffset);
2,155✔
2694
              char* dest = GET_ROWCELL_INTERBUF(pEntryInfo);
2,155✔
2695
              if (pVal->isNull) {
2,155✔
2696
                pEntryInfo->isNullRes = 1;
×
2697
              } else {
2698
                pEntryInfo->isNullRes = 0;
2,155✔
2699
                if (IS_VAR_DATA_TYPE(pVal->data.type)) {
2,155✔
2700
                  memcpy(dest, pVal->data.pData, pVal->data.nData);
×
2701
                } else {
2702
                  memcpy(dest, &pVal->data.val, pExprInfo[j].base.resSchema.bytes);
2,155✔
2703
                }
2704
              }
2705
            }
2706
          }
2707
        }
2708
      }
2709
    }
2710
  }
2711

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

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

2720
  pBlock->info.rows += pTmpRow->numOfRows;
13,792✔
2721
  TAOS_CHECK_EXIT(extWinApplyAggPostProjection(pOperator, pExtW, pBlock, startRow, pTmpRow->numOfRows));
13,792✔
2722
  if (extWinUsesValueFillMode(pExtW->fillMode)) {
27,584✔
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));
13,792✔
2727

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

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

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

2744
_exit:
1,599,149✔
2745

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

2750
  return code;
1,599,149✔
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,119,703,994✔
2762
    TSDB_CHECK_NULL(pResultRow, code, lino, _exit, TSDB_CODE_INVALID_PARA);
1,119,703,994✔
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) {
10,735,626✔
2802
  if (pExtW->resWinIdx <= 0 || (pExtW->multiTableMode && !pExtW->inputHasOrder)) {
10,735,626✔
2803
    return false;
4,313,250✔
2804
  }
2805

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

2810
  if (NULL == pExtW->timeRangeExpr || !pExtW->timeRangeExpr->needCalc) {
6,419,820✔
2811
    return true;
6,419,820✔
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,315,806✔
2833
  SList** ppList = NULL;
4,315,806✔
2834
  if (taosArrayGetSize(pOutputBlocks) > winIdx) {
4,315,806✔
2835
    ppList = taosArrayGet(pOutputBlocks, winIdx);
×
2836
    extWinRecycleBlockList(pExtW, ppList);
×
2837
  } else {
2838
    ppList = taosArrayReserve(pOutputBlocks, 1);
4,315,806✔
2839
  }
2840

2841
  return ppList;
4,315,806✔
2842
}
2843

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

2847
  *ppList = taosArrayGetP(pExtW->pOutputBlocks, winIdx);
6,422,376✔
2848
  if (*ppList != NULL) {
6,422,376✔
2849
    return code;
6,422,376✔
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) {
10,738,182✔
2865
  SExternalWindowOperator* pExtW = pOperator->info;
10,738,182✔
2866
  SList*                   pList = NULL;
10,738,182✔
2867
  int32_t                  code = TSDB_CODE_SUCCESS, lino = 0;
10,738,182✔
2868
  
2869
  if (pWin->resWinIdx >= 0) {
10,738,182✔
2870
    TAOS_CHECK_EXIT(extWinEnsureBlockList(pExtW, pWin->resWinIdx, &pList));
2,556✔
2871
  } else {
2872
    if (extWinLastWinClosed(pExtW)) {
10,735,626✔
2873
      pWin->resWinIdx = pExtW->pTGrpCtx->pCCtx->lastWinIdx;
6,419,820✔
2874
      TAOS_CHECK_EXIT(extWinEnsureBlockList(pExtW, pWin->resWinIdx, &pList));
6,419,820✔
2875
    } else {
2876
      pWin->resWinIdx = pExtW->resWinIdx++;
4,315,806✔
2877
      pList = tdListNew(POINTER_BYTES * 2);
4,315,806✔
2878
      TSDB_CHECK_NULL(pList, code, lino, _exit, terrno);
4,315,806✔
2879
      SList** ppList = extWinReserveGetBlockList(pExtW, pExtW->pOutputBlocks, pWin->resWinIdx);
4,315,806✔
2880
      TSDB_CHECK_NULL(ppList, code, lino, _exit, terrno);
4,315,806✔
2881
      *ppList = pList;
4,315,806✔
2882
    }
2883
  }
2884

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

2889
_exit:
10,737,330✔
2890

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

2895
  return code;
10,737,756✔
2896
}
2897

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

2907
  qDebug("%s %s win[%" PRId64 ", %" PRId64 "] got res block %p winRowIdx %p, winOutIdx:%d, capacity:%d", 
10,737,756✔
2908
      pOperator->pTaskInfo->id.str, __func__, pWin->tw.skey, pWin->tw.ekey, pResBlock, pIdx, pWin->resWinIdx, pResBlock->info.capacity);
2909
  
2910
  if (!pExtW->pTmpBlock) {
10,738,182✔
2911
    TAOS_CHECK_EXIT(createOneDataBlock(pInputBlock, false, &pExtW->pTmpBlock));
29,394✔
2912
  } else {
2913
    blockDataCleanup(pExtW->pTmpBlock);
10,708,788✔
2914
  }
2915
  
2916
  TAOS_CHECK_EXIT(blockDataEnsureCapacity(pExtW->pTmpBlock, TMAX(1, rows)));
10,738,182✔
2917

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

2921
  qDebug("%s %s start to apply project to tmp blk", pOperator->pTaskInfo->id.str, __func__);
10,738,182✔
2922
  TAOS_CHECK_EXIT(projectApplyFunctionsWithSelect(pExprSup->pExprInfo, pResBlock, pExtW->pTmpBlock, pExprSup->pCtx,
10,738,182✔
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;
10,724,124✔
2928
  pResBlock->info.id.baseGId = pInputBlock->info.id.baseGId;
10,737,330✔
2929

2930
  TAOS_CHECK_EXIT(extWinAppendWinIdx(pOperator->pTaskInfo, pIdx, pResBlock, extWinGetCurWinIdx(pOperator), rows));
10,737,330✔
2931

2932
_exit:
10,726,254✔
2933

2934
  if (code) {
10,720,716✔
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__);
10,720,716✔
2938
  }
2939
  
2940
  return code;
10,736,904✔
2941
}
2942

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

2957
    qDebug("%s ext window [%" PRId64 ", %" PRId64 ") tgrp %" PRId64 " project start, ascScan:%d, startPos:%d, winRows:%d",
10,738,182✔
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));
10,738,182✔
2961
    
2962
    startPos += winRows;
10,738,182✔
2963
  }
2964
  
2965
_exit:
38,340✔
2966

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

2971
  return code;
38,340✔
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,280,448✔
3139
  SExtWinCalcGrpCtx*  pCCtx = pExtW->pTGrpCtx->pCCtx;
4,280,448✔
3140
  int32_t         numOfWin = taosArrayGetSize(pCCtx->pWins);
4,280,448✔
3141
  int32_t         code = TSDB_CODE_SUCCESS;
4,280,448✔
3142
  int32_t         lino = 0;
4,280,448✔
3143
  SSDataBlock*    pRes = NULL;
4,280,448✔
3144

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

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

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

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

3167
    break;
4,272,354✔
3168
  }
3169

3170
_exit:
8,094✔
3171

3172
  *ppRes = pRes;
4,280,448✔
3173

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

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

3187
  if (pStream->curGrpCalc) {
4,292,802✔
3188
    TAOS_CHECK_EXIT(extWinNonAggOutputSingleGrpRes(pOperator, pExtW, &pBlock));
4,272,354✔
3189
  }
3190
  if (pBlock == NULL || pBlock->info.rows == 0) {
4,292,802✔
3191
    pStream->curGrpCalc = tSimpleHashIterate(pStream->pGroupCalcInfos, pStream->curGrpCalc, &pExtW->lastOutputIter);
28,542✔
3192
    while (pStream->curGrpCalc != NULL) {
52,824✔
3193
      if (pStream->curGrpCalc->pRunnerGrpCtx) {
32,376✔
3194
        pExtW->lastTGrpId = *(uint64_t*)tSimpleHashGetKey(pStream->curGrpCalc, NULL);
16,188✔
3195
        pExtW->pTGrpCtx = pStream->curGrpCalc->pRunnerGrpCtx;
8,094✔
3196
        pExtW->ownTGrpCtx = false;
8,094✔
3197
        
3198
        TAOS_CHECK_EXIT(extWinNonAggOutputSingleGrpRes(pOperator, pExtW, &pBlock));
8,094✔
3199
        if (pBlock != NULL && pBlock->info.rows > 0) {
8,094✔
3200
          break;
8,094✔
3201
        }
3202
      }
3203
      
3204
      pStream->curGrpCalc = tSimpleHashIterate(pStream->pGroupCalcInfos, pStream->curGrpCalc, &pExtW->lastOutputIter);
24,282✔
3205
    }
3206
  }
3207

3208
  extWinPostUpdateStreamRt(pStream, pOperator, pExtW);
4,292,802✔
3209

3210
_exit:
4,292,802✔
3211

3212
  *ppRes = pBlock;
4,292,802✔
3213

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

3218
  return code;
4,292,802✔
3219
}
3220

3221

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

3230
  if (pStream->isMultiGroupCalc) {
4,372,038✔
3231
    TAOS_CHECK_EXIT(extWinNonAggOutputMultiGrpRes(pOperator, pExtW, &pRes));
4,292,802✔
3232
  } else {
3233
    for (; pExtW->outWinIdx < numOfWin; pExtW->outWinIdx++, extWinIncCurWinOutIdx(pOperator)) {
79,236✔
3234
      SList* pList = taosArrayGetP(pExtW->pOutputBlocks, pExtW->outWinIdx);
43,878✔
3235
      if (listNEles(pList) <= 0) {
43,878✔
3236
        continue;
×
3237
      }
3238

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

3244
      if (listNEles(pList) <= 0) {
43,878✔
3245
        pExtW->outWinIdx++;
42,600✔
3246
        extWinIncCurWinOutIdx(pOperator);
42,600✔
3247
      }
3248

3249
      break;
43,878✔
3250
    }
3251
  }
3252

3253
  if (pRes) {
4,372,038✔
3254
    qDebug("%s result generated, rows:%" PRId64 , GET_TASKID(pOperator->pTaskInfo), pRes->info.rows);
4,316,232✔
3255
    pRes->info.version = pOperator->pTaskInfo->version;
4,316,232✔
3256
    pRes->info.dataLoad = 1;
4,316,232✔
3257
  } else {
3258
    pStream->pStreamBlkWinIdx = NULL;
55,806✔
3259
    qDebug("%s ext window done", GET_TASKID(pOperator->pTaskInfo));
55,806✔
3260
  }
3261

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

3264
_exit:
4,372,038✔
3265

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

3270
  return code;
4,372,038✔
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) {
135,165✔
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) {
135,165✔
3287
    goto _exit;
135,165✔
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) {
3,898,384✔
3338
  SExternalWindowOperator* pExtW = (SExternalWindowOperator*)pOperator->info;
3,898,384✔
3339
  int32_t                  startPos = 0, winRows = 0;
3,898,384✔
3340
  int64_t*                 tsCol = extWinExtractTsCol(pInputBlock, pExtW->primaryTsIndex, pOperator->pTaskInfo);
3,898,384✔
3341
  bool                     ascScan = pExtW->binfo.inputTsOrder == TSDB_ORDER_ASC;
3,898,384✔
3342
  int32_t                  code = 0, lino = 0;
3,898,384✔
3343
  SExtWinTimeWindow*       pWin = NULL;
3,898,384✔
3344
  bool                     scalarCalc = false;
3,898,384✔
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;
3,898,384✔
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,767,632✔
3359
        SExprSupp* pScalarSup = &pExtW->scalarSupp;
3,408✔
3360
        TAOS_CHECK_EXIT(projectApplyFunctions(pScalarSup->pExprInfo, pInputBlock, pInputBlock, pScalarSup->pCtx, pScalarSup->numOfExprs,
3,408✔
3361
                                     pExtW->pPseudoColInfo, GET_STM_RTINFO(pOperator->pTaskInfo), pOperator->pTaskInfo));
3362
      }
3363

3364
      scalarCalc = true;
3,767,632✔
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) {
599,182✔
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:
3,898,384✔
3388

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

3393
  return code;
3,898,384✔
3394
}
3395

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

3412
  if (pExtW->fillMode == FILL_MODE_NONE) {
1,734,703✔
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;
334,335,648✔
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) {
334,335,648✔
3430
          continue;
136,776✔
3431
        }
3432
        if (pBlock->info.rows + 1 > pBlock->info.capacity) {
334,198,872✔
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) {
866,823,804✔
3437
          SColumnInfoData* pColInfo = taosArrayGet(pBlock->pDataBlock, j);
532,624,932✔
3438
          if (pColInfo) {
532,624,932✔
3439
            colDataSetNULL(pColInfo, pBlock->info.rows);
532,624,932✔
3440
          }
3441
        }
3442
        pBlock->info.rows += 1;
334,198,872✔
3443
        pCCtx->outWinNum++;
334,198,872✔
3444
        TAOS_CHECK_EXIT(extWinAppendWinIdx(pOperator->pTaskInfo, pExtW->pWinRowIdx, pBlock, pCCtx->outWinIdx, 1));
334,198,872✔
3445

3446
        if (pBlock->info.rows >= pOperator->resultInfo.threshold) {
334,198,872✔
3447
          ++pCCtx->outWinIdx;
69,741✔
3448
          break;
69,741✔
3449
        }
3450
        continue;
334,129,131✔
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,289,051✔
3457
            pBlock->info.rows + pRow->numOfRows + (pExtW->isDynWindow ? numOfWin - pCCtx->outWinIdx
1,289,051✔
3458
                                                                       : pCCtx->outWinTotalNum - pCCtx->outWinNum);
171,272✔
3459
        TAOS_CHECK_EXIT(blockDataEnsureCapacity(pBlock, newSize));
1,289,051✔
3460
        qDebug("datablock capacity not sufficient, expand to required:%d, current capacity:%d, %s", newSize,
1,289,051✔
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,040,178✔
3474
        break;
1,040,178✔
3475
      }
3476
    }
3477

3478
    if (grpDone) {
1,646,618✔
3479
      *grpDone = pExtW->isDynWindow ? (pCCtx->outWinIdx >= numOfWin)
33,228✔
3480
                                    : (pCCtx->outWinIdx >= numOfWin || pCCtx->outWinNum >= pCCtx->outWinTotalNum);
16,614✔
3481
    }
3482

3483
    return code;
1,646,618✔
3484
  }
3485

3486
  bool               emitAllWins = extWinShouldEmitAllWindows(pExtW, pCCtx);
88,085✔
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) {
266,119✔
3491
    SExtWinTimeWindow* pWin = TARRAY_GET_ELEM(pCCtx->pWins, pCCtx->outWinIdx);
178,034✔
3492
    bool emptyWin = false;
178,034✔
3493
    SResultRow* pRow = NULL;
178,034✔
3494

3495
    if (pWin->resWinIdx < 0) {
178,034✔
3496
      emptyWin = true;
90,510✔
3497
    } else {
3498
      pRow = extWinGetResultRowByIdx(pExtW, pWin->resWinIdx, pExtW->aggSup.resultRowSize);
87,524✔
3499
      doUpdateNumOfRows(pCtx, pRow, numOfExprs, rowEntryOffset);
87,524✔
3500
      if (!extWinRowHasSourceData(pRow)) {
87,524✔
3501
        emptyWin = true;
×
3502
      }
3503
    }
3504

3505
    if (emptyWin) {
178,034✔
3506
      if (!emitAllWins) {
90,510✔
3507
        continue;
×
3508
      }
3509

3510
      SResultRow* pFillRow = NULL;
90,510✔
3511
      if (pExtW->fillMode == FILL_MODE_PREV) {
90,510✔
3512
        pFillRow = extWinFindAdjacentFillRow(pExtW, pCCtx, pCCtx->outWinIdx, false);
16,809✔
3513
      } else if (pExtW->fillMode == FILL_MODE_NEXT) {
73,701✔
3514
        pFillRow = extWinFindAdjacentFillRow(pExtW, pCCtx, pCCtx->outWinIdx, true);
6,896✔
3515
      }
3516
      TAOS_CHECK_EXIT(extWinAppendAggFilledRow(pOperator, pExtW, pCCtx, pWin, pFillRow));
90,510✔
3517
      pCCtx->outWinNum++;
90,510✔
3518

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

3526
    pCCtx->outWinNum++;
87,524✔
3527

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

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

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

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

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

3550
  if (grpDone) {
88,085✔
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,704✔
3553
                                                   : (pCCtx->outWinIdx >= numOfWin || pCCtx->outWinNum >= pCCtx->outWinTotalNum);
1,704✔
3554
  }
3555

3556
_exit:
87,233✔
3557

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

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

3570
  if (pTGrpCtx == NULL) {
211,521✔
3571
    return TSDB_CODE_SUCCESS;
43,452✔
3572
  }
3573

3574
  if (pTGrpCtx->pCCtx) {
168,069✔
3575
    pExtW->lastCGrpId = pTGrpCtx->pCCtx->groupId;
97,258✔
3576
    TAOS_CHECK_EXIT(extWinAggOutputSingleCGrpRes(pOperator, pExtW, NULL));
97,258✔
3577
  }
3578
  if (0 == pBlock->info.rows) {
168,069✔
3579
    pTGrpCtx->pCCtx = tSimpleHashIterate(pTGrpCtx->pCGCtxs, pTGrpCtx->pCCtx, &pTGrpCtx->lastCtxIter);
167,217✔
3580
    while (pTGrpCtx->pCCtx != NULL) {
274,569✔
3581
      pExtW->lastCGrpId = pTGrpCtx->pCCtx->groupId;
203,758✔
3582
      TAOS_CHECK_EXIT(extWinAggOutputSingleCGrpRes(pOperator, pExtW, NULL));
203,758✔
3583
      if (pBlock->info.rows > 0) {
203,758✔
3584
        break;
96,406✔
3585
      }
3586
      
3587
      pTGrpCtx->pCCtx = tSimpleHashIterate(pTGrpCtx->pCGCtxs, pTGrpCtx->pCCtx, &pTGrpCtx->lastCtxIter);
107,352✔
3588
    }
3589
  }
3590

3591
_exit:
71,663✔
3592

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

3597
  return code;
168,069✔
3598
}
3599

3600

3601

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

3608
  if (pStream->curGrpCalc) {
63,900✔
3609
    pExtW->lastTGrpId = *(uint64_t*)tSimpleHashGetKey(pStream->curGrpCalc, NULL);
37,488✔
3610
    pExtW->pTGrpCtx = pStream->curGrpCalc->pRunnerGrpCtx;
18,744✔
3611
    pExtW->ownTGrpCtx = false;
18,744✔
3612
    if (pExtW->calcWithPartition) {
18,744✔
3613
      TAOS_CHECK_EXIT(extWinAggOutputMulNoOrderCGrpsRes(pOperator, pExtW));
18,744✔
3614
    } else {
3615
      TAOS_CHECK_EXIT(extWinAggOutputSingleCGrpRes(pOperator, pExtW, NULL));
×
3616
    }
3617
  }
3618
  if (0 == pBlock->info.rows) {
63,900✔
3619
    pStream->curGrpCalc = tSimpleHashIterate(pStream->pGroupCalcInfos, pStream->curGrpCalc, &pExtW->lastOutputIter);
63,048✔
3620
    while (pStream->curGrpCalc != NULL) {
144,840✔
3621
      if (pStream->curGrpCalc->pRunnerGrpCtx) {
99,684✔
3622
        pExtW->lastTGrpId = *(uint64_t*)tSimpleHashGetKey(pStream->curGrpCalc, NULL);
35,784✔
3623
        pExtW->pTGrpCtx = pStream->curGrpCalc->pRunnerGrpCtx;
17,892✔
3624
        pExtW->ownTGrpCtx = false;
17,892✔
3625
        pExtW->lastCGrpId = 0;
17,892✔
3626

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

3648
  extWinPostUpdateStreamRt(pStream, pOperator, pExtW);
63,900✔
3649

3650
_exit:
63,900✔
3651

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

3656
  return code;
63,900✔
3657
}
3658

3659

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

3667
  if (pTGrpCtx == NULL || pTGrpCtx->pCGCtxs == NULL) {
43,452✔
3668
    return TSDB_CODE_SUCCESS;
14,910✔
3669
  }
3670

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

3685
_exit:
28,542✔
3686

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

3691
  return code;
28,542✔
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) {
9,798✔
3732
  int32_t code = 0, lino = 0;
9,798✔
3733
  SExtWinTrigGrpCtx* pTGrpCtx = pExtW->pTGrpCtx;
9,798✔
3734
  SSDataBlock*    pBlock = pExtW->binfo.pRes;
9,798✔
3735
  int32_t grpNum = taosArrayGetSize(pExtW->pCTGrpIds);
9,798✔
3736
  SStreamRuntimeFuncInfo* pStream = &pOperator->pTaskInfo->pStreamRuntimeInfo->funcInfo;
9,798✔
3737
  bool grpDone = false;
9,798✔
3738

3739
  for (; pExtW->lastGrpIdx < grpNum; ++pExtW->lastGrpIdx) {
9,798✔
3740
    uint64_t* cGrpId = taosArrayGet(pExtW->pCTGrpIds, pExtW->lastGrpIdx);
2,982✔
3741
    uint64_t* tGrpId = cGrpId + 1;
2,982✔
3742

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

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

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

3761
  extWinPostUpdateStreamRt(pStream, pOperator, pExtW);
9,798✔
3762
  
3763
_exit:
9,798✔
3764

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

3769
  return code;
9,798✔
3770
}
3771

3772

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

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

3785
  if (pExtW->needGroupSort) {
1,821,998✔
3786
    if (pStream && pStream->isMultiGroupCalc) {
53,250✔
3787
      if (pExtW->calcWithPartition) {
9,798✔
3788
        TAOS_CHECK_EXIT(extWinAggOutputMulOrderTCGrpsRes(pOperator, pExtW));
9,798✔
3789
      } else {
3790
        TAOS_CHECK_EXIT(extWinAggOutputMulOrderTGrpsRes(pOperator, pExtW));
×
3791
      }
3792
    } else {
3793
      TAOS_CHECK_EXIT(extWinAggOutputMulOrderCGrpsRes(pOperator, pExtW));
43,452✔
3794
    }
3795
  } else {
3796
    if (pStream && pStream->isMultiGroupCalc) {
1,768,748✔
3797
      TAOS_CHECK_EXIT(extWinAggOutputMulNoOrderTGrpsRes(pOperator, pExtW));
63,900✔
3798
    } else if (pExtW->calcWithPartition) {
1,704,848✔
3799
      TAOS_CHECK_EXIT(extWinAggOutputMulNoOrderCGrpsRes(pOperator, pExtW));
174,885✔
3800
    } else {
3801
      TAOS_CHECK_EXIT(extWinAggOutputSingleCGrpRes(pOperator, pExtW, NULL));
1,529,963✔
3802
    }
3803
  }
3804
  
3805
  qDebug("%s result generated, rows:%" PRId64 ", groupId:%" PRIu64, GET_TASKID(pTaskInfo), pBlock->info.rows,
1,821,998✔
3806
         pBlock->info.id.groupId);
3807

3808
  int32_t rowsBeforeFilter = pBlock->info.rows;
1,821,998✔
3809
  SColumnInfoData* pFilterRes = NULL;
1,821,998✔
3810
  SColMatchInfo* pMatchInfo = pExtW->fillMatchInfo.pList != NULL ? &pExtW->fillMatchInfo : NULL;
1,821,998✔
3811
  TAOS_CHECK_EXIT(doFilter(pBlock, pOperator->exprSupp.pFilterInfo, pMatchInfo, &pFilterRes));
1,821,998✔
3812
  if (pBlock->info.rows < rowsBeforeFilter) {
1,821,998✔
3813
    if (pFilterRes != NULL) {
8,159✔
3814
      TAOS_CHECK_EXIT(extWinRebuildWinIdxByFilter(pTaskInfo, pExtW->pWinRowIdx, rowsBeforeFilter, pFilterRes));
8,159✔
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,821,998✔
3822
  extWinAssignBlockGrpId(pOperator, pExtW, &pBlock->info.id);
1,821,998✔
3823

3824
  qDebug("%s result generated, rows:%" PRId64 ", cGrpId:%" PRIu64 " baseGid:%" PRIu64, 
1,821,998✔
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,821,998✔
3828

3829
  if (*ppRes) {
1,821,998✔
3830
    (*ppRes)->info.window.skey = pExtW->orgTableTimeRange.skey;
1,399,888✔
3831
    (*ppRes)->info.window.ekey = pExtW->orgTableTimeRange.ekey;
1,399,888✔
3832
  }
3833
  if (pOperator->pTaskInfo->pStreamRuntimeInfo) {
1,821,998✔
3834
    pOperator->pTaskInfo->pStreamRuntimeInfo->funcInfo.pStreamBlkWinIdx = pExtW->pWinRowIdx;
659,782✔
3835
  }
3836

3837
_exit:
1,821,998✔
3838
  colDataDestroy(pFilterRes);
1,821,998✔
3839
  taosMemoryFree(pFilterRes);
1,821,998✔
3840

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

3845
  return code;
1,821,998✔
3846
}
3847

3848
static void extWinFreeResultRow(SExternalWindowOperator* pExtW) {
477,916✔
3849
  if (pExtW->resultRows.resRowAllcNum * pExtW->aggSup.resultRowSize >= 1048576) {
477,916✔
3850
    int32_t i = 1;
31,950✔
3851
    while (i < pExtW->resultRows.resRowsSize && pExtW->resultRows.pResultRows[i]) {
31,950✔
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) {
477,916✔
3859
    blockDataFreeCols(pExtW->binfo.pRes);
×
3860
  }
3861
}
477,916✔
3862

3863
static bool extWinNonAggGotResBlock(SOperatorInfo* pOperator, SExternalWindowOperator* pExtW) {
38,340✔
3864
  if (pExtW->calcWithPartition) {
38,340✔
3865
    return false;
19,596✔
3866
  }
3867

3868
  if ((pExtW->multiTableMode && !pExtW->inputHasOrder) || pExtW->needGroupSort) {
18,744✔
3869
    return false;
12,780✔
3870
  }
3871
  int32_t remainWin = pExtW->resWinIdx - pExtW->outWinIdx;
5,964✔
3872
  if (remainWin > 1 && (NULL == pExtW->timeRangeExpr || !pExtW->timeRangeExpr->needCalc)) {
5,964✔
3873
    return true;
×
3874
  }
3875
  
3876
  SList* pList = taosArrayGetP(pExtW->pOutputBlocks, pExtW->outWinIdx);
5,964✔
3877
  if (!pList || listNEles(pList) <= 0) {
5,964✔
3878
    return false;
×
3879
  }
3880
  if (listNEles(pList) > 1) {
5,964✔
3881
    return true;
426✔
3882
  }
3883

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

3891
  return false;
3,408✔
3892
}
3893

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

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

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

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

3917
  return code;
3,550,418✔
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,643,540✔
3924
  if (pTaskInfo == NULL || pTaskInfo->pStreamRuntimeInfo == NULL) {
1,643,540✔
3925
    return;
1,162,216✔
3926
  }
3927

3928
  SStreamRuntimeFuncInfo* pInfo = &pTaskInfo->pStreamRuntimeInfo->funcInfo;
481,324✔
3929
  if (!pInfo->isMultiGroupCalc) {
481,324✔
3930
    return;
408,904✔
3931
  }
3932

3933
  pInfo->pStreamPesudoFuncVals = NULL;
72,420✔
3934
  pInfo->pStreamPartColVals = NULL;
72,420✔
3935
}
3936

3937

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

3946
  if (NULL == pExtW->pEmptyInputBlock) {
51,972✔
3947
    goto _exit;
51,972✔
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:
51,972✔
3974

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

3979
  return code;
51,972✔
3980
}
3981

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

3988
  if (pOperator == NULL || pOperator->pTaskInfo == NULL || pOperator->pTaskInfo->pStreamRuntimeInfo == NULL) {
1,640,984✔
3989
    return;
1,162,216✔
3990
  }
3991

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

3994
  pStream->curGrpCalc = NULL;
478,768✔
3995
  pStream->curGrpRead = NULL;
478,768✔
3996

3997
  if (pExtW->needGroupSort) {
478,768✔
3998
    if (pStream->isMultiGroupCalc && pExtW->calcWithPartition) {
35,784✔
3999
      if (pExtW->pCTGrpIds) {
6,816✔
4000
        taosArraySort(pExtW->pCTGrpIds, extWinGrpIdCompare);
2,982✔
4001
      }
4002
    } else {
4003
      if (pExtW->pGrpIds) {
28,968✔
4004
        taosArraySort(pExtW->pGrpIds, extWinGrpIdCompare);
14,058✔
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) {
478,768✔
4012
    pExtW->pTGrpCtx = NULL;
72,420✔
4013
    pExtW->ownTGrpCtx = false;
72,420✔
4014
  }
4015

4016
  if ((!pStream->isMultiGroupCalc) && pExtW->calcWithPartition && pExtW->pTGrpCtx) {
478,768✔
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) {
75,071✔
4022
      extWinDestroyCGrpCtx(pExtW->pTGrpCtx->pCCtx);
3,428✔
4023
      taosMemoryFree(pExtW->pTGrpCtx->pCCtx);
3,428✔
4024
    }
4025
    pExtW->pTGrpCtx->pCCtx = NULL;
75,071✔
4026
  }
4027
}
4028

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

4038
  if (pExtW->pTGrpCtx->pCCtx == NULL) {
1,163,508✔
4039
    pExtW->pTGrpCtx->pCCtx = taosMemoryCalloc(1, sizeof(*pExtW->pTGrpCtx->pCCtx));
1,119,071✔
4040
    TSDB_CHECK_NULL(pExtW->pTGrpCtx->pCCtx, code, lino, _exit, terrno);
1,119,071✔
4041
    extWinInitDynParamCGrpCtx(pExtW->pTGrpCtx->pCCtx);
1,119,071✔
4042
  }
4043

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

4049
  return code;
1,163,508✔
4050
}
4051

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

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

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

4071
    TAOS_CHECK_EXIT(extWinEnsureDynParamOpenCtx(pExtW));
1,163,508✔
4072

4073
    if (pExtW->pTGrpCtx->pCCtx->pWins) {
1,163,508✔
4074
      taosArrayDestroy(pExtW->pTGrpCtx->pCCtx->pWins);
44,437✔
4075
    }
4076

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

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

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

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

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

4106
  while (1) {
3,960,580✔
4107
    SSDataBlock* pBlock = getNextBlockFromDownstreamRemainDetach(pOperator, 0);
5,605,412✔
4108
    if (pBlock == NULL) {
5,604,120✔
4109
      if (EEXT_MODE_AGG == pExtW->mode) {
1,640,984✔
4110
        if (pInfo && pInfo->isMultiGroupCalc) {
1,584,326✔
4111
          TAOS_CHECK_EXIT(extWinAggHandleMultiTGrpEmptyWins(pOperator, pExtW));
51,972✔
4112
        } else {
4113
          if (pExtW->pEmptyInputBlock && pExtW->pTGrpCtx == NULL) {
1,532,354✔
4114
            pExtW->pTGrpCtx = taosMemoryCalloc(1, sizeof(*pExtW->pTGrpCtx));
14,985✔
4115
            TSDB_CHECK_NULL(pExtW->pTGrpCtx, code, lino, _exit, terrno);
14,985✔
4116
            pExtW->ownTGrpCtx = true;
14,985✔
4117
            pExtW->pTGrpCtx->pCCtx = taosMemoryCalloc(1, sizeof(*pExtW->pTGrpCtx->pCCtx));
14,985✔
4118
            TSDB_CHECK_NULL(pExtW->pTGrpCtx->pCCtx, code, lino, _exit, terrno);
14,985✔
4119
            TAOS_CHECK_EXIT(extWinInitCGrpCtx(pExtW, pOperator->pTaskInfo, pExtW->pTGrpCtx->pCCtx));
14,985✔
4120
          }
4121
          TAOS_CHECK_EXIT(extWinAggHandleEmptyWins(pOperator, pBlock, true, NULL));
1,532,354✔
4122
        }
4123
      }
4124

4125
      break;
1,640,984✔
4126
    }
4127

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

4137
    printDataBlock(pBlock, __func__, pTaskInfo->id.str, pTaskInfo->id.queryId);
3,963,136✔
4138

4139
    qInfo("%s ext window mode:%s baseGrp:%" PRIu64 " grp:%" PRIu64 " got %" PRId64 " rows from downstream",
3,963,136✔
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 &&
3,963,136✔
4148
        pTaskInfo->pStreamRuntimeInfo->funcInfo.isMultiGroupCalc &&
398,660✔
4149
        pTaskInfo->pStreamRuntimeInfo->funcInfo.pGroupCalcInfos) {
43,026✔
4150
      if (pExtW->calcWithPartition && pBlock->info.id.groupId != 0 &&
86,052✔
4151
          tSimpleHashGet(pTaskInfo->pStreamRuntimeInfo->funcInfo.pGroupCalcInfos,
43,026✔
4152
                         &pBlock->info.id.groupId, sizeof(pBlock->info.id.groupId)) != NULL) {
43,026✔
4153
        pBlock->info.id.baseGId = pBlock->info.id.groupId;
29,820✔
4154
        qDebug("%s extWin fallback baseGId <- matched groupId %" PRIu64,
29,820✔
4155
               GET_TASKID(pTaskInfo), pBlock->info.id.groupId);
4156
      } else {
4157
        int32_t __iter = 0;
13,206✔
4158
        int32_t __size = tSimpleHashGetSize(pTaskInfo->pStreamRuntimeInfo->funcInfo.pGroupCalcInfos);
13,206✔
4159
        if (__size == 1) {
13,206✔
4160
          SSTriggerGroupCalcInfo* __one = tSimpleHashIterate(pTaskInfo->pStreamRuntimeInfo->funcInfo.pGroupCalcInfos, NULL, &__iter);
5,112✔
4161
          if (__one) {
5,112✔
4162
            uint64_t __gid = *(uint64_t*)tSimpleHashGetKey(__one, NULL);
5,112✔
4163
            pBlock->info.id.baseGId = __gid;
5,112✔
4164
            qDebug("%s extWin fallback baseGId set to single gid %" PRIu64, GET_TASKID(pTaskInfo), __gid);
5,112✔
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 &&
3,963,136✔
4173
        pTaskInfo->pStreamRuntimeInfo->funcInfo.isMultiGroupCalc &&
412,718✔
4174
        pExtW->calcWithPartition &&
57,084✔
4175
        pBlock->info.id.groupId != 0 && pBlock->info.id.baseGId == 0) {
57,084✔
4176
      qDebug("%s skip block: no matched trigger group for groupId %" PRIu64,
8,094✔
4177
             GET_TASKID(pTaskInfo), pBlock->info.id.groupId);
4178
      continue;
8,094✔
4179
    }
4180

4181
    if (pTaskInfo->pStreamRuntimeInfo &&
3,955,042✔
4182
        pTaskInfo->pStreamRuntimeInfo->funcInfo.isMultiGroupCalc &&
404,624✔
4183
        pExtW->calcWithPartition &&
48,990✔
4184
        pBlock->info.id.groupId != 0 && pBlock->info.id.baseGId != 0 &&
48,990✔
4185
        pBlock->info.id.groupId != pBlock->info.id.baseGId) {
48,990✔
4186
      qDebug("%s skip block: baseGId %" PRIu64 " != groupId %" PRIu64,
18,318✔
4187
             GET_TASKID(pTaskInfo), pBlock->info.id.baseGId, pBlock->info.id.groupId);
4188
      continue;
18,318✔
4189
    }
4190

4191
    TAOS_CHECK_EXIT(extWinSwitchInitCtxs(pExtW, pTaskInfo, &pBlock->info.id));
3,936,724✔
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);
3,936,724✔
4197

4198
    switch (pExtW->mode) {
3,936,724✔
4199
      case EEXT_MODE_SCALAR:
38,340✔
4200
        TAOS_CHECK_EXIT(extWinProjectOpen(pOperator, pBlock));
38,340✔
4201
        if (extWinNonAggGotResBlock(pOperator, pExtW)) {
38,340✔
4202
          goto _exit;
2,556✔
4203
        }
4204
        break;
35,784✔
4205
      case EEXT_MODE_AGG:
3,898,384✔
4206
        TAOS_CHECK_EXIT(extWinAggOpen(pOperator, pBlock));
3,898,384✔
4207
        break;
3,898,384✔
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,640,984✔
4220
    freeOperatorParam(pOperator->pOperatorGetParam, OP_GET_PARAM);
×
4221
    pOperator->pOperatorGetParam = NULL;
×
4222
  }
4223
  OPTR_SET_OPENED(pOperator);
1,640,984✔
4224

4225
  extWinPrepareForOutput(pOperator, pExtW);
1,640,984✔
4226

4227
_exit:
1,643,540✔
4228

4229
  extWinEndClearCtxs(pExtW, pTaskInfo);
1,643,540✔
4230

4231
  if (code != 0) {
1,643,540✔
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,643,540✔
4238
}
4239

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

4250
  if (pOperator->pOperatorGetParam) {
6,195,328✔
4251
    if (pOperator->status == OP_EXEC_DONE) {
1,163,508✔
4252
      pOperator->status = OP_NOT_OPENED;
44,437✔
4253
    }
4254
  }
4255

4256
  extWinRecycleBlkNode(pExtW, &pExtW->pLastBlkNode);
6,195,328✔
4257

4258
  while (1) {
4259
    if (pOperator->status == OP_NOT_OPENED) {
6,195,328✔
4260
      TAOS_CHECK_EXIT(pOperator->fpSet._openFn(pOperator));
1,644,832✔
4261
    }
4262

4263
    if (pExtW->mode == EEXT_MODE_SCALAR || pExtW->mode == EEXT_MODE_INDEFR_FUNC) {
6,194,036✔
4264
      TAOS_CHECK_EXIT(extWinNonAggOutputRes(pOperator, ppRes));
4,372,038✔
4265
      if (NULL == *ppRes) {
4,372,038✔
4266
        setOperatorCompleted(pOperator);
55,806✔
4267
        extWinFreeResultRow(pExtW);
55,806✔
4268
      }
4269
    } else {
4270
      TAOS_CHECK_EXIT(extWinAggOutputRes(pOperator, ppRes));
1,821,998✔
4271
      if (NULL != *ppRes) {
1,821,998✔
4272
        break;
1,399,888✔
4273
      }
4274

4275
      if (pExtW->pTGrpCtx == NULL || pExtW->pTGrpCtx->pCCtx == NULL ||
422,110✔
4276
          (pExtW->isDynWindow ? (pExtW->pTGrpCtx->pCCtx->outWinIdx >= taosArrayGetSize(pExtW->pTGrpCtx->pCCtx->pWins))
148,097✔
4277
                              : (pExtW->pTGrpCtx->pCCtx->outWinNum >= pExtW->pTGrpCtx->pCCtx->outWinTotalNum))) {
148,097✔
4278
        setOperatorCompleted(pOperator);
422,110✔
4279
        if (pTaskInfo->pStreamRuntimeInfo) {
422,110✔
4280
          extWinFreeResultRow(pExtW);
422,110✔
4281
        }
4282
        break;
422,110✔
4283
      }
4284
    }
4285
    break;
4,372,038✔
4286
  }
4287

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

4296
_exit:
477,916✔
4297

4298
  if (code) {
6,194,036✔
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,194,036✔
4305
    *ppRes = NULL;
×
4306
  }
4307

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

4312
  return code;
6,194,036✔
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,599,575✔
4325
                                     SOperatorInfo** pOptrOut) {
4326
  SExternalWindowPhysiNode* pPhynode = (SExternalWindowPhysiNode*)pNode;
1,599,575✔
4327
  QRY_PARAM_CHECK(pOptrOut);
1,599,575✔
4328
  int32_t                  code = 0;
1,599,575✔
4329
  int32_t                  lino = 0;
1,599,575✔
4330
  bool                     isInStream = true;
1,599,575✔
4331
  STimeWindow              nonStreamExtWinRange = {.skey = INT64_MAX, .ekey = INT64_MIN};
1,599,575✔
4332
  SExternalWindowOperator* pExtW = taosMemoryCalloc(1, sizeof(SExternalWindowOperator));
1,600,001✔
4333
  SOperatorInfo*           pOperator = taosMemoryCalloc(1, sizeof(SOperatorInfo));
1,599,575✔
4334
  pOperator->pPhyNode = pNode;
1,599,575✔
4335
  if (!pExtW || !pOperator) {
1,599,575✔
UNCOV
4336
    code = terrno;
×
4337
    lino = __LINE__;
×
4338
    goto _error;
×
4339
  }
4340
  initOperatorCostInfo(pOperator);
1,599,575✔
4341
  pExtW->pTaskInfo = pTaskInfo;
1,600,001✔
4342
  pExtW->needGroupSort = pPhynode->needGroupSort;
1,600,001✔
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,600,001✔
4347
  // pExtW->calcWithPartition = (pTaskInfo->execModel == OPTR_EXEC_MODEL_STREAM) ? pPhynode->calcWithPartition : false;
4348
  pExtW->extWinSplit = pPhynode->extWinSplit;
1,599,575✔
4349
  
4350
  setOperatorInfo(pOperator, "ExternalWindowOperator", QUERY_NODE_PHYSICAL_PLAN_EXTERNAL_WINDOW, true, OP_NOT_OPENED,
1,600,001✔
4351
                  pExtW, pTaskInfo);
4352
                  
4353
  SSDataBlock* pResBlock = createDataBlockFromDescNode(pPhynode->window.node.pOutputDataBlockDesc);
1,600,001✔
4354
  QUERY_CHECK_NULL(pResBlock, code, lino, _error, terrno);
1,600,001✔
4355
  initBasicInfo(&pExtW->binfo, pResBlock);
1,600,001✔
4356

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

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

4369
  if (pTaskInfo->pStreamRuntimeInfo != NULL){
1,600,001✔
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,600,001✔
4377
    isInStream = false;
1,600,001✔
4378
    // If pre-init has not been performed, initialize from subquery blocks here.
4379
    if (pTaskInfo->pStreamRuntimeInfo == NULL ||
1,600,001✔
4380
        pTaskInfo->pStreamRuntimeInfo->funcInfo.pGroupCalcInfos == NULL) {
×
4381
      code = extWinInitNonStreamWindowDataFromBlock(pPhynode, pTaskInfo, &nonStreamExtWinRange);
1,600,001✔
4382
      if (code != TSDB_CODE_SUCCESS) {
1,600,001✔
4383
        lino = __LINE__;
852✔
4384
        goto _error;
852✔
4385
      }
4386
    }
4387
  }
4388

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

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

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

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

4417
    if (pPhynode->window.pProjs != NULL) {
1,542,491✔
4418
      code = extWinInitExprSupp(&pExtW->projSupp, pPhynode->window.pProjs, pTaskInfo);
2,586✔
4419
      QUERY_CHECK_CODE(code, lino, _error);
2,586✔
4420
    }
4421
    
4422
    size_t keyBufSize = sizeof(int64_t) * 2 + POINTER_BYTES;
1,542,491✔
4423
    initResultSizeInfo(&pOperator->resultInfo, 4096);
1,542,491✔
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,542,491✔
4428
    TSDB_CHECK_NULL(pExtW->pWinRowIdx, code, lino, _error, terrno);
1,542,491✔
4429
    
4430
    int32_t num = 0;
1,542,491✔
4431
    SExprInfo* pExprInfo = NULL;
1,542,491✔
4432
    code = createExprInfo(pPhynode->window.pFuncs, NULL, &pExprInfo, &num);
1,542,491✔
4433
    QUERY_CHECK_CODE(code, lino, _error);
1,542,491✔
4434
    pOperator->exprSupp.hasWindow = true;
1,542,491✔
4435
    pOperator->exprSupp.hasWindowOrGroup = true;
1,542,491✔
4436
    code = initAggSup(&pOperator->exprSupp, &pExtW->aggSup, pExprInfo, num, keyBufSize, pTaskInfo->id.str, 0, 0);
1,542,491✔
4437
    QUERY_CHECK_CODE(code, lino, _error);
1,542,491✔
4438

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

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

4446
    if (pExtW->fillMode != FILL_MODE_NONE) {
1,542,491✔
4447
      pExtW->pFillRowBuf = taosMemoryMalloc(pExtW->aggSup.resultRowSize);
47,641✔
4448
      QUERY_CHECK_NULL(pExtW->pFillRowBuf, code, lino, _error, terrno);
47,641✔
4449
    }
4450

4451
    nodesWalkExprs(pPhynode->window.pFuncs, extWinHasCountLikeFunc, &pExtW->hasCountFunc);
1,542,491✔
4452
    if (pExtW->fillMode != FILL_MODE_NONE || (pExtW->hasCountFunc && pTaskInfo->execModel == OPTR_EXEC_MODEL_STREAM)) {
1,542,065✔
4453
      code = extWinCreateEmptyInputBlock(pOperator, &pExtW->pEmptyInputBlock);
48,067✔
4454
      QUERY_CHECK_CODE(code, lino, _error);
47,641✔
4455
      qDebug("%s ext window prepared empty input block, fillMode:%d", pOperator->pTaskInfo->id.str, pExtW->fillMode);
47,641✔
4456
    } else {
4457
      qDebug("%s ext window have CountLikeFunc: %d. IsInStream:%d", pOperator->pTaskInfo->id.str, pExtW->hasCountFunc,
1,493,998✔
4458
             pTaskInfo->execModel == OPTR_EXEC_MODEL_STREAM);
4459
    }
4460

4461
    code = initExecTimeWindowInfo(&pExtW->twAggSup.timeWindowData, &pTaskInfo->window);
1,542,491✔
4462
    QUERY_CHECK_CODE(code, lino, _error);
1,542,491✔
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,599,149✔
4505
  if (pExtW->timeRangeExpr) {
1,599,149✔
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,599,149✔
4511
    if (!isInStream) {
979,430✔
4512
      pExtW->getWinFp = extWinGetOvlpWin;
979,430✔
4513
      pExtW->multiTableMode = false;
979,430✔
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) {
619,719✔
4524
      pExtW->getWinFp = extWinGetMultiTbOvlpWin;
619,719✔
4525
      pExtW->multiTableMode = true;
619,719✔
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,599,149✔
4536
  pExtW->orgTableUid = pPhynode->orgTableUid;
1,599,149✔
4537
  pExtW->orgTableVgId = pPhynode->orgTableVgId;
1,599,149✔
4538

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

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

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

4555
  *pOptrOut = pOperator;
1,599,149✔
4556

4557
  qDebug("%s extWin operator created, mode:%s, multiTableMnode:%d, inputHasOrder:%d, hasTimeRangeExpr:%d, timeRangeNeedCalc:%d "
1,599,149✔
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,599,149✔
4563

4564
_error:
852✔
4565

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

4570
  destroyOperatorAndDownstreams(pOperator, &pDownstream, 1);
852✔
4571
  pTaskInfo->code = code;
852✔
4572
  qError("error happens at %s %d, code:%s", __func__, lino, tstrerror(code));
852✔
4573
  return code;
852✔
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,
647,464✔
4596
                                            SColumnInfoData** ppEndCol, int32_t* pNumRows, int32_t* pNumCols) {
4597
  int32_t code = TSDB_CODE_SUCCESS;
647,464✔
4598
  int32_t lino = 0;
647,464✔
4599

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

4607
  int32_t numRows = pBlock->info.rows;
647,464✔
4608
  int32_t numCols = taosArrayGetSize(pBlock->pDataBlock);
647,464✔
4609
  if (numCols < 2) {
647,464✔
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);
647,464✔
4615
  SColumnInfoData* pEndCol = (SColumnInfoData*)taosArrayGet(pBlock->pDataBlock, 1);
647,464✔
4616
  TSDB_CHECK_NULL(pStartCol, code, lino, _exit, TSDB_CODE_INVALID_PARA);
647,464✔
4617
  TSDB_CHECK_NULL(pEndCol, code, lino, _exit, TSDB_CODE_INVALID_PARA);
647,464✔
4618
  if (pStartCol == NULL || pEndCol == NULL || pStartCol->info.type != TSDB_DATA_TYPE_TIMESTAMP ||
647,464✔
4619
      pEndCol->info.type != TSDB_DATA_TYPE_TIMESTAMP) {
647,464✔
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;
647,464✔
4625
  *ppEndCol = pEndCol;
647,464✔
4626
  *pNumRows = numRows;
647,464✔
4627
  *pNumCols = numCols;
647,464✔
4628

4629
_exit:
647,464✔
4630
  return code;
647,464✔
4631
}
4632

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

4641
  return TSDB_CODE_SUCCESS;
58,666,932✔
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,
58,542,114✔
4652
                                                   SArray** ppExternalData) {
4653
  int32_t code = TSDB_CODE_SUCCESS;
58,542,114✔
4654
  int32_t lino = 0;
58,542,114✔
4655
  SArray* pExternalData = taosArrayInit(numCols - 2, sizeof(SStreamGroupValue));
58,542,114✔
4656
  TSDB_CHECK_NULL(pBlock, code, lino, _exit, TSDB_CODE_INVALID_PARA);
57,974,682✔
4657
  TSDB_CHECK_NULL(pBlock->pDataBlock, code, lino, _exit, TSDB_CODE_INVALID_PARA);
57,974,682✔
4658
  TSDB_CHECK_NULL(ppExternalData, code, lino, _exit, TSDB_CODE_INVALID_PARA);
58,047,528✔
4659
  TSDB_CHECK_NULL(pExternalData, code, lino, _exit, terrno);
58,047,528✔
4660

4661
  for (int32_t col = 2; col < numCols; ++col) {
115,329,108✔
4662
    SColumnInfoData* pColData = (SColumnInfoData*)taosArrayGet(pBlock->pDataBlock, col);
56,754,618✔
4663
    if (pColData == NULL) {
56,871,342✔
4664
      continue;
×
4665
    }
4666

4667
    SStreamGroupValue data = {0};
56,871,342✔
4668
    SValue* pVal = &data.data;
56,881,992✔
4669

4670
    pVal->type = pColData->info.type;
56,881,992✔
4671

4672
    if (!colDataIsNull_s(pColData, row)) {
114,478,812✔
4673
      void* pData = colDataGetData(pColData, row);
57,359,112✔
4674
      if (IS_VAR_DATA_TYPE(pVal->type) || pVal->type == TSDB_DATA_TYPE_DECIMAL) {
57,380,432✔
4675
        int32_t datumLen = IS_VAR_DATA_TYPE(pVal->type) ? calcStrBytesByType(pVal->type, (char*)pData) : pColData->info.bytes;
20✔
4676
        void* pDataCopy = taosMemoryMalloc(datumLen);
69,884✔
4677
        TSDB_CHECK_NULL(pDataCopy, code, lino, _exit, terrno);
69,884✔
4678
        memcpy(pDataCopy, pData, datumLen);
69,884✔
4679
        valueSetDatum(pVal, pVal->type, pDataCopy, datumLen);
69,884✔
4680
      } else {
4681
        valueSetDatum(pVal, pVal->type, pData, pColData->info.bytes);
57,261,112✔
4682
      }
4683
    } else {
4684
      data.isNull = true;
6,816✔
4685
    }
4686

4687
    TSDB_CHECK_NULL(taosArrayPush(pExternalData, &data), code, lino, _exit, terrno);
57,242,388✔
4688
  }
4689

4690
  *ppExternalData = pExternalData;
58,574,490✔
4691

4692
_exit:
58,537,002✔
4693
  if (code) {
58,537,002✔
4694
    taosArrayDestroyEx(pExternalData, extWinDestroyExternalDataValue);
×
4695
  }
4696
  return code;
58,564,266✔
4697
}
4698

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

4708
  *pParam = (SSTriggerCalcParam){0};
58,528,908✔
4709

4710
  if (!colDataIsNull_s(pStartCol, row)) {
117,327,048✔
4711
    pParam->wstart = *(int64_t*)colDataGetData(pStartCol, row);
58,708,254✔
4712
  }
4713

4714
  if (!colDataIsNull_s(pEndCol, row)) {
117,263,148✔
4715
    pParam->wend = *(int64_t*)colDataGetData(pEndCol, row);
58,609,422✔
4716
  }
4717

4718
  pParam->wduration = pParam->wend - pParam->wstart;
58,591,530✔
4719
  TAOS_CHECK_EXIT(extWinBuildExternalWindowDataForRow(pBlock, numCols, row, &pParam->pExternalWindowData));
58,549,782✔
4720

4721
_exit:
58,534,446✔
4722
  return code;
58,534,446✔
4723
}
4724

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

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

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

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

4742
  int32_t blockNum = taosArrayGetSize(pBlocks);
219,059✔
4743
  for (int32_t blockIdx = 0; blockIdx < blockNum; ++blockIdx) {
365,698✔
4744
    SSDataBlock** ppOne = taosArrayGet(pBlocks, blockIdx);
220,763✔
4745
    if (ppOne == NULL || *ppOne == NULL) {
220,763✔
4746
      continue;
×
4747
    }
4748

4749
    if (extWinGetRemoteResultGroupId(*ppOne) != 0) {
220,763✔
4750
      return true;
74,124✔
4751
    }
4752
  }
4753

4754
  return false;
144,935✔
4755
}
4756

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

4761
  code = extWinCheckMonotonicWstart(*pHasPrevStart, *pPrevStart, pParam->wstart, row);
24,347,094✔
4762
  if (code != TSDB_CODE_SUCCESS) {
24,280,212✔
4763
    return code;
852✔
4764
  }
4765

4766
  *pPrevStart = pParam->wstart;
24,279,360✔
4767
  *pHasPrevStart = true;
24,341,130✔
4768

4769
  void* pRet = taosArrayPush(pRt->pStreamPesudoFuncVals, pParam);
24,340,704✔
4770
  if (pRet == NULL) {
24,009,276✔
4771
    return terrno;
×
4772
  }
4773

4774
  return TSDB_CODE_SUCCESS;
24,009,276✔
4775
}
4776

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

4783
  if (pGroupInfo == NULL) {
34,326,654✔
4784
    SSTriggerGroupCalcInfo info = {0};
146,970✔
4785
    info.pParams = taosArrayInit(4, sizeof(SSTriggerCalcParam));
146,970✔
4786
    TSDB_CHECK_NULL(info.pParams, code, lino, _exit, terrno);
146,970✔
4787

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

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

4794
  *ppGroupInfo = pGroupInfo;
34,327,932✔
4795

4796
_exit:
34,326,654✔
4797
  return code;
34,326,654✔
4798
}
4799

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

4808
  TAOS_CHECK_EXIT(extWinGetOrCreateGroupedCalcInfo(pRt, groupId, &pGroupInfo));
34,261,902✔
4809

4810
  groupHasPrevStart = (taosArrayGetSize(pGroupInfo->pParams) > 0);
34,326,228✔
4811
  if (groupHasPrevStart) {
34,311,318✔
4812
    SSTriggerCalcParam* pLast = taosArrayGetLast(pGroupInfo->pParams);
34,163,922✔
4813
    TSDB_CHECK_NULL(pLast, code, lino, _exit, TSDB_CODE_QRY_EXECUTOR_INTERNAL_ERROR);
34,172,868✔
4814
    groupPrevStart = pLast->wstart;
34,172,868✔
4815
  }
4816

4817
  code = extWinCheckMonotonicWstart(groupHasPrevStart, groupPrevStart, pParam->wstart, row);
34,320,264✔
4818
  if (code != TSDB_CODE_SUCCESS) {
34,313,022✔
4819
    TAOS_CHECK_EXIT(code);
×
4820
  }
4821

4822
  TSDB_CHECK_NULL(taosArrayPush(pGroupInfo->pParams, pParam), code, lino, _exit, terrno);
68,580,462✔
4823

4824
_exit:
34,267,440✔
4825
  return code;
34,223,562✔
4826
}
4827

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

4841
  for (int32_t blockIdx = 0; blockIdx < blockNum; ++blockIdx) {
938,366✔
4842
    SSDataBlock** ppOne = taosArrayGet(pBlocks, blockIdx);
473,230✔
4843
    TSDB_CHECK_NULL(ppOne, code, lino, _exit, TSDB_CODE_INVALID_PARA);
473,230✔
4844
    SSDataBlock* pBlock = *ppOne;
473,230✔
4845
    TSDB_CHECK_NULL(pBlock, code, lino, _exit, TSDB_CODE_INVALID_PARA);
473,230✔
4846

4847
    TAOS_CHECK_EXIT(extWinValidateNonStreamBlock(pBlock, &pStartCol, &pEndCol, &numRows, &numCols));
473,230✔
4848

4849
    uint64_t groupId = extWinGetRemoteResultGroupId(pBlock);
473,230✔
4850
    if (prevGroupId != groupId) {
473,230✔
4851
      hasPrevStart = false;
463,432✔
4852
      prevGroupId = groupId;
463,432✔
4853
    }
4854

4855
    for (int32_t row = 0; row < numRows; ++row) {
24,710,842✔
4856
      SSTriggerCalcParam param = {0};
24,238,038✔
4857
      TAOS_CHECK_EXIT(extWinBuildTriggerParamForRow(pBlock, pStartCol, pEndCol, numCols, row, &param));
24,215,034✔
4858

4859
      pExtWinTimeRange->skey = TMIN(pExtWinTimeRange->skey, param.wstart);
24,204,810✔
4860
      pExtWinTimeRange->ekey = TMAX(pExtWinTimeRange->ekey, param.wend);
24,208,644✔
4861

4862
      code = extWinAppendNonGroupedCalcParam(pRt, &param, &hasPrevStart, &prevStart, row);
24,226,110✔
4863
      if (code != TSDB_CODE_SUCCESS) {
24,178,824✔
4864
        tDestroySSTriggerCalcParam(&param);
852✔
4865
        TAOS_CHECK_EXIT(code);
852✔
4866
      }
4867
    }
4868
  }
4869

4870
_exit:
466,840✔
4871
  return code;
465,988✔
4872
}
4873

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

4884
  for (int32_t blockIdx = 0; blockIdx < blockNum; ++blockIdx) {
248,358✔
4885
    SSDataBlock** ppOne = taosArrayGet(pBlocks, blockIdx);
174,234✔
4886
    TSDB_CHECK_NULL(ppOne, code, lino, _exit, TSDB_CODE_INVALID_PARA);
174,234✔
4887
    SSDataBlock* pBlock = *ppOne;
174,234✔
4888
    TSDB_CHECK_NULL(pBlock, code, lino, _exit, TSDB_CODE_INVALID_PARA);
174,234✔
4889

4890
    TAOS_CHECK_EXIT(extWinValidateNonStreamBlock(pBlock, &pStartCol, &pEndCol, &numRows, &numCols));
174,234✔
4891

4892
    uint64_t groupId = extWinGetRemoteResultGroupId(pBlock);
174,234✔
4893
    for (int32_t row = 0; row < numRows; ++row) {
34,415,688✔
4894
      SSTriggerCalcParam param = {0};
34,243,158✔
4895
      TAOS_CHECK_EXIT(extWinBuildTriggerParamForRow(pBlock, pStartCol, pEndCol, numCols, row, &param));
34,241,880✔
4896

4897
      pExtWinTimeRange->skey = TMIN(pExtWinTimeRange->skey, param.wstart);
34,250,400✔
4898
      pExtWinTimeRange->ekey = TMAX(pExtWinTimeRange->ekey, param.wend);
34,322,394✔
4899

4900
      code = extWinAppendGroupedCalcParam(pRt, groupId, &param, row);
34,318,134✔
4901
      if (code != TSDB_CODE_SUCCESS) {
34,241,454✔
4902
        tDestroySSTriggerCalcParam(&param);
×
4903
        TAOS_CHECK_EXIT(code);
×
4904
      }
4905
    }
4906
  }
4907

4908
_exit:
72,846✔
4909
  return code;
74,124✔
4910
}
4911

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

4918
  if (NULL == pPhynode->pSubquery || nodeType(pPhynode->pSubquery) != QUERY_NODE_REMOTE_TABLE) {
1,660,493✔
4919
    qDebug("invalid subquery in external window, pSubquery:%p, type:%d", pPhynode->pSubquery,
1,120,381✔
4920
           pPhynode->pSubquery ? nodeType(pPhynode->pSubquery) : -1);
4921
    return TSDB_CODE_SUCCESS;
1,120,381✔
4922
  }
4923
  TSDB_CHECK_NULL(pTaskInfo, code, lino, _exit, TSDB_CODE_INVALID_PARA);
540,112✔
4924

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

4931
  // Initialize basic runtime function parameters.
4932
  SStreamRuntimeFuncInfo* pRt = &pTaskInfo->pStreamRuntimeInfo->funcInfo;
540,112✔
4933
  pRt->withExternalWindow = true;
540,112✔
4934
  pRt->isWindowTrigger = true;
540,112✔
4935
  pRt->triggerType = STREAM_TRIGGER_SESSION;
540,112✔
4936
  pRt->precision = 0;
540,112✔
4937
  pRt->curIdx = 0;
540,112✔
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;
540,112✔
4950
  TAOS_CHECK_EXIT(qFetchRemoteNode(gTaskScalarExtra.pSubJobCtx, pRemote->subQIdx, (SNode*)pRemote));
540,112✔
4951

4952
  pBlocks = pRemote->pResBlks;
540,112✔
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);
540,112✔
4960
  pRt->isMultiGroupCalc = groupedRemoteResult ? 1 : 0;
540,112✔
4961
  pRt->curGrpCalc = NULL;
540,112✔
4962
  pRt->groupId = 0;
540,112✔
4963

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

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

4977
  if (groupedRemoteResult) {
540,112✔
4978
    pRt->pGroupCalcInfos = tSimpleHashInit(256, taosGetDefaultHashFunction(TSDB_DATA_TYPE_UBIGINT));
74,124✔
4979
    TSDB_CHECK_NULL(pRt->pGroupCalcInfos, code, lino, _exit, terrno);
74,124✔
4980
    tSimpleHashSetFreeFp(pRt->pGroupCalcInfos, tDestroySSTriggerGroupCalcInfo);
74,124✔
4981
  }
4982
  
4983
  if (groupedRemoteResult) {
540,112✔
4984
    TAOS_CHECK_EXIT(extWinBuildGroupedCalcInfosFromBlocks(pBlocks, pRt, &extWinTimeRange));
74,124✔
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);
74,124✔
4989
    pRt->pStreamPesudoFuncVals = NULL;
74,124✔
4990
  } else {
4991
    TAOS_CHECK_EXIT(extWinBuildNonGroupedCalcInfosFromBlocks(pBlocks, pRt, &extWinTimeRange));
465,988✔
4992
  }
4993

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

4998
  if (!groupedRemoteResult && taosArrayGetSize(pRt->pStreamPesudoFuncVals) > 0) {
539,260✔
4999
    qInfo("%s non-stream extWin mock initialized from block, winNum:%d, firstWin:[%" PRId64 ", %" PRId64 "], wholeRange:[%" PRId64 ", %" PRId64 "]",
462,580✔
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) {
76,680✔
5005
    qInfo("%s non-stream extWin initialized from grouped remote result, groupNum:%d, wholeRange:[%" PRId64 ", %" PRId64 "]",
74,124✔
5006
          GET_TASKID(pTaskInfo), pRt->pGroupCalcInfos ? tSimpleHashGetSize(pRt->pGroupCalcInfos) : 0,
5007
          extWinTimeRange.skey, extWinTimeRange.ekey);
5008
  }
5009

5010
_exit:
540,112✔
5011
  if (pBlocks) {
540,112✔
5012
    for (int32_t blockIdx = 0; blockIdx < taosArrayGetSize(pBlocks); ++blockIdx) {
1,196,096✔
5013
      SSDataBlock** ppOne = taosArrayGet(pBlocks, blockIdx);
658,540✔
5014
      if (ppOne && *ppOne) {
658,540✔
5015
        blockDataDestroy(*ppOne);
658,540✔
5016
      }
5017
    }
5018
    taosArrayDestroy(pBlocks);
537,556✔
5019
  }
5020
  return code;
540,112✔
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