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

taosdata / TDengine / #4473

08 Jul 2025 09:38AM UTC coverage: 62.922% (+0.7%) from 62.22%
#4473

push

travis-ci

web-flow
Merge pull request #31712 from taosdata/merge/mainto3.0

merge: from main to 3.0 branch

158525 of 321496 branches covered (49.31%)

Branch coverage included in aggregate %.

56 of 60 new or added lines in 13 files covered. (93.33%)

1333 existing lines in 67 files now uncovered.

245526 of 320647 relevant lines covered (76.57%)

17689640.25 hits per line

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

74.08
/source/libs/planner/src/planOptimizer.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 <stdint.h>
17
#include <string.h>
18
#include "filter.h"
19
#include "functionMgt.h"
20
#include "nodes.h"
21
#include "osMemPool.h"
22
#include "parser.h"
23
#include "planInt.h"
24
#include "systable.h"
25
#include "tarray.h"
26
#include "tglobal.h"
27
#include "ttime.h"
28
#include "scalar.h"
29

30
#define OPTIMIZE_FLAG_MASK(n) (1 << n)
31

32
#define OPTIMIZE_FLAG_SCAN_PATH       OPTIMIZE_FLAG_MASK(0)
33
#define OPTIMIZE_FLAG_PUSH_DOWN_CONDE OPTIMIZE_FLAG_MASK(1)
34
#define OPTIMIZE_FLAG_STB_JOIN        OPTIMIZE_FLAG_MASK(2)
35
#define OPTIMIZE_FLAG_ELIMINATE_PROJ  OPTIMIZE_FLAG_MASK(3)
36
#define OPTIMIZE_FLAG_JOIN_COND       OPTIMIZE_FLAG_MASK(4)
37

38
#define OPTIMIZE_FLAG_SET_MASK(val, mask)   (val) |= (mask)
39
#define OPTIMIZE_FLAG_CLEAR_MASK(val, mask) (val) &= (~(mask))
40
#define OPTIMIZE_FLAG_TEST_MASK(val, mask)  (((val) & (mask)) != 0)
41

42
typedef struct SOptimizeContext {
43
  SPlanContext* pPlanCxt;
44
  bool          optimized;
45
} SOptimizeContext;
46

47
typedef int32_t (*FOptimize)(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan);
48

49
typedef struct SOptimizeRule {
50
  char*     pName;
51
  FOptimize optimizeFunc;
52
} SOptimizeRule;
53

54
typedef struct SOptimizePKCtx {
55
  SNodeList* pList;
56
  int32_t    code;
57
} SOptimizePKCtx;
58

59
typedef enum EScanOrder { SCAN_ORDER_ASC = 1, SCAN_ORDER_DESC, SCAN_ORDER_BOTH } EScanOrder;
60

61
typedef struct SOsdInfo {
62
  SScanLogicNode* pScan;
63
  SNodeList*      pSdrFuncs;
64
  SNodeList*      pDsoFuncs;
65
  EScanOrder      scanOrder;
66
} SOsdInfo;
67

68
typedef struct SCpdIsMultiTableCondCxt {
69
  SSHashObj* pLeftTbls;
70
  SSHashObj* pRightTbls;
71
  bool       havaLeftCol;
72
  bool       haveRightCol;
73
  bool       condIsNull;
74
} SCpdIsMultiTableCondCxt;
75

76
typedef struct SCpdIsMultiTableResCxt {
77
  SSHashObj* pLeftTbls;
78
  SSHashObj* pRightTbls;
79
  bool       haveLeftCol;
80
  bool       haveRightCol;
81
  bool       leftColOp;
82
  bool       rightColOp;
83
  bool       leftColNonNull;
84
  bool       rightColNonNull;
85
} SCpdIsMultiTableResCxt;
86

87
typedef struct SCpdCollRewriteTableColsCxt {
88
  int32_t    code;
89
  SSHashObj* pLeftTbls;
90
  SSHashObj* pRightTbls;
91
  SSHashObj* pLeftCols;
92
  SSHashObj* pRightCols;
93
} SCpdCollRewriteTableColsCxt;
94

95
typedef struct SCpdCollectTableColCxt {
96
  SSHashObj* pTables;
97
  SNodeList* pResCols;
98
  SHashObj*  pColHash;
99
  int32_t    errCode;
100
} SCpdCollectTableColCxt;
101

102
typedef struct SCollectColsCxt {
103
  SHashObj*  pColHash;
104
  int32_t    errCode;
105
} SCollectColsCxt;
106

107

108
typedef enum ECondAction {
109
  COND_ACTION_STAY = 1,
110
  COND_ACTION_PUSH_JOIN,
111
  COND_ACTION_PUSH_LEFT_CHILD,
112
  COND_ACTION_PUSH_RIGHT_CHILD
113
  // after supporting outer join, there are other possibilities
114
} ECondAction;
115

116
#define PUSH_DOWN_LEFT_FLT  (1 << 0)
117
#define PUSH_DOWN_RIGHT_FLT (1 << 1)
118
#define PUSH_DOWN_ON_COND   (1 << 2)
119
#define PUSH_DONW_FLT_COND  (PUSH_DOWN_LEFT_FLT | PUSH_DOWN_RIGHT_FLT)
120
#define PUSH_DOWN_ALL_COND  (PUSH_DOWN_LEFT_FLT | PUSH_DOWN_RIGHT_FLT | PUSH_DOWN_ON_COND)
121

122
typedef struct SJoinOptimizeOpt {
123
  int8_t pushDownFlag;
124
} SJoinOptimizeOpt;
125

126
typedef bool (*FMayBeOptimized)(SLogicNode* pNode, void* pCtx);
127
typedef bool (*FShouldBeOptimized)(SLogicNode* pNode, void* pInfo);
128

129
#if 0
130
static SJoinOptimizeOpt gJoinOpt[JOIN_TYPE_MAX_VALUE][JOIN_STYPE_MAX_VALUE] = {
131
           /* NONE                OUTER                  SEMI                  ANTI                   ANY                    ASOF                   WINDOW */
132
/*INNER*/  {{PUSH_DOWN_ALL_COND}, {0},                   {0},                  {0},                   {PUSH_DOWN_ALL_COND},  {0},                   {0}},
133
/*LEFT*/   {{0},                  {PUSH_DOWN_LEFT_FLT},  {PUSH_DOWN_ALL_COND}, {PUSH_DOWN_LEFT_FLT},  {PUSH_DOWN_LEFT_FLT},  {PUSH_DOWN_LEFT_FLT},  {PUSH_DOWN_LEFT_FLT}},
134
/*RIGHT*/  {{0},                  {PUSH_DOWN_RIGHT_FLT}, {PUSH_DOWN_ALL_COND}, {PUSH_DOWN_RIGHT_FLT}, {PUSH_DOWN_RIGHT_FLT}, {PUSH_DOWN_RIGHT_FLT}, {PUSH_DOWN_RIGHT_FLT}},
135
/*FULL*/   {{0},                  {0},                   {0},                  {0},                   {0},                   {0},                   {0}},
136
};
137
#else
138
static SJoinOptimizeOpt gJoinWhereOpt[JOIN_TYPE_MAX_VALUE][JOIN_STYPE_MAX_VALUE] = {
139
    /* NONE                OUTER                  SEMI                  ANTI                   ASOF WINDOW */
140
    /*INNER*/ {{PUSH_DOWN_ALL_COND}, {0}, {0}, {0}, {0}, {0}},
141
    /*LEFT*/
142
    {{0}, {PUSH_DOWN_LEFT_FLT}, {PUSH_DOWN_LEFT_FLT}, {PUSH_DOWN_LEFT_FLT}, {PUSH_DOWN_LEFT_FLT}, {PUSH_DOWN_LEFT_FLT}},
143
    /*RIGHT*/
144
    {{0},
145
     {PUSH_DOWN_RIGHT_FLT},
146
     {PUSH_DOWN_RIGHT_FLT},
147
     {PUSH_DOWN_RIGHT_FLT},
148
     {PUSH_DOWN_RIGHT_FLT},
149
     {PUSH_DOWN_RIGHT_FLT}},
150
    /*FULL*/ {{0}, {0}, {0}, {0}, {0}, {0}},
151
};
152

153
static SJoinOptimizeOpt gJoinOnOpt[JOIN_TYPE_MAX_VALUE][JOIN_STYPE_MAX_VALUE] = {
154
    /* NONE                OUTER                  SEMI                  ANTI                   ASOF WINDOW */
155
    /*INNER*/ {{PUSH_DONW_FLT_COND}, {0}, {0}, {0}, {0}, {0}},
156
    /*LEFT*/ {{0}, {PUSH_DOWN_RIGHT_FLT}, {PUSH_DONW_FLT_COND}, {PUSH_DOWN_RIGHT_FLT}, {0}, {0}},
157
    /*RIGHT*/ {{0}, {PUSH_DOWN_LEFT_FLT}, {PUSH_DONW_FLT_COND}, {PUSH_DOWN_LEFT_FLT}, {0}, {0}},
158
    /*FULL*/ {{0}, {0}, {0}, {0}, {0}, {0}},
159
};
160

161
#endif
162

163
static SLogicNode* optFindPossibleNode(SLogicNode* pNode, FMayBeOptimized func, void* pCtx) {
147,813,326✔
164
  if (func(pNode, pCtx)) {
147,813,326✔
165
    return pNode;
2,406,373✔
166
  }
167
  SNode* pChild;
168
  FOREACH(pChild, pNode->pChildren) {
250,126,654✔
169
    SLogicNode* pScanNode = optFindPossibleNode((SLogicNode*)pChild, func, pCtx);
109,601,385✔
170
    if (NULL != pScanNode) {
109,604,335✔
171
      return pScanNode;
4,887,783✔
172
    }
173
  }
174
  return NULL;
140,525,269✔
175
}
176

177
static bool optFindEligibleNode(SLogicNode* pNode, FShouldBeOptimized func, void* pInfo) {
13,657,554✔
178
  if (func(pNode, pInfo)) {
13,657,554!
179
    return true;
×
180
  }
181
  SNode* pChild;
182
  FOREACH(pChild, pNode->pChildren) {
23,879,057✔
183
    if (optFindEligibleNode((SLogicNode*)pChild, func, pInfo)) {
10,221,414!
184
      return true;
×
185
    }
186
  }
187
  return false;
13,657,643✔
188
}
189

190
static void optResetParent(SLogicNode* pNode) {
22,626✔
191
  SNode* pChild = NULL;
22,626✔
192
  FOREACH(pChild, pNode->pChildren) { ((SLogicNode*)pChild)->pParent = pNode; }
45,252!
193
}
22,626✔
194

195
static EDealRes optRebuildTbanme(SNode** pNode, void* pContext) {
490,928✔
196
  if (QUERY_NODE_COLUMN == nodeType(*pNode) && COLUMN_TYPE_TBNAME == ((SColumnNode*)*pNode)->colType) {
490,928✔
197
    SFunctionNode* pFunc = NULL;
129,665✔
198
    int32_t        code = nodesMakeNode(QUERY_NODE_FUNCTION, (SNode**)&pFunc);
129,665✔
199
    if (NULL == pFunc) {
129,665!
200
      *(int32_t*)pContext = code;
×
201
      return DEAL_RES_ERROR;
×
202
    }
203
    tstrncpy(pFunc->functionName, "tbname", TSDB_FUNC_NAME_LEN);
129,665✔
204
    pFunc->funcType = FUNCTION_TYPE_TBNAME;
129,665✔
205
    pFunc->node.resType = ((SColumnNode*)*pNode)->node.resType;
129,665✔
206
    nodesDestroyNode(*pNode);
129,665✔
207
    *pNode = (SNode*)pFunc;
129,665✔
208
    return DEAL_RES_IGNORE_CHILD;
129,665✔
209
  }
210
  return DEAL_RES_CONTINUE;
361,263✔
211
}
212

213
static void optSetParentOrder(SLogicNode* pNode, EOrder order, SLogicNode* pNodeForcePropagate) {
2,467,407✔
214
  if (NULL == pNode) {
2,467,407✔
215
    return;
210,305✔
216
  }
217
  pNode->inputTsOrder = order;
2,257,102✔
218
  switch (nodeType(pNode)) {
2,257,102✔
219
    // for those nodes that will change the order, stop propagating
220
    // case QUERY_NODE_LOGIC_PLAN_WINDOW:
221
    case QUERY_NODE_LOGIC_PLAN_AGG:
970,349✔
222
    case QUERY_NODE_LOGIC_PLAN_SORT:
223
    case QUERY_NODE_LOGIC_PLAN_FILL:
224
      if (pNode == pNodeForcePropagate) {
970,349✔
225
        pNode->outputTsOrder = order;
129,758✔
226
        break;
129,758✔
227
      } else
228
        return;
840,591✔
229
    case QUERY_NODE_LOGIC_PLAN_JOIN:
300,902✔
230
      pNode->outputTsOrder = order;
300,902✔
231
      break;
300,902✔
232
    case QUERY_NODE_LOGIC_PLAN_WINDOW:
49,245✔
233
      // Window output ts order default to be asc, and changed when doing sort by primary key optimization.
234
      // We stop propagate the original order to parents.
235
      // Use window output ts order instead.
236
      order = pNode->outputTsOrder;
49,245✔
237
      break;
49,245✔
238
    case QUERY_NODE_LOGIC_PLAN_PROJECT:
720,910✔
239
      if (projectCouldMergeUnsortDataBlock((SProjectLogicNode*)pNode)) {
720,910✔
240
        pNode->outputTsOrder = TSDB_ORDER_NONE;
291,866✔
241
        return;
291,866✔
242
      }
243
      pNode->outputTsOrder = order;
429,054✔
244
      break;
429,054✔
245
    default:
215,696✔
246
      pNode->outputTsOrder = order;
215,696✔
247
      break;
215,696✔
248
  }
249
  optSetParentOrder(pNode->pParent, order, pNodeForcePropagate);
1,124,655✔
250
}
251

252
EDealRes scanPathOptHaveNormalColImpl(SNode* pNode, void* pContext) {
736,385✔
253
  if (QUERY_NODE_COLUMN == nodeType(pNode)) {
736,385✔
254
    *((bool*)pContext) =
504,112✔
255
        (COLUMN_TYPE_TAG != ((SColumnNode*)pNode)->colType && COLUMN_TYPE_TBNAME != ((SColumnNode*)pNode)->colType);
504,112✔
256
    return *((bool*)pContext) ? DEAL_RES_END : DEAL_RES_IGNORE_CHILD;
504,112✔
257
  }
258
  return DEAL_RES_CONTINUE;
232,273✔
259
}
260

261
static bool scanPathOptHaveNormalCol(SNodeList* pList) {
946,123✔
262
  bool res = false;
946,123✔
263
  nodesWalkExprsPostOrder(pList, scanPathOptHaveNormalColImpl, &res);
946,123✔
264
  return res;
946,136✔
265
}
266

267
static bool scanPathOptMayBeOptimized(SLogicNode* pNode, void* pCtx) {
16,456,142✔
268
  if (OPTIMIZE_FLAG_TEST_MASK(pNode->optimizedFlag, OPTIMIZE_FLAG_SCAN_PATH)) {
16,456,142✔
269
    return false;
3,684,045✔
270
  }
271
  if (QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(pNode)) {
12,772,097✔
272
    return false;
11,601,958✔
273
  }
274
  return true;
1,170,139✔
275
}
276

277
static bool scanPathOptShouldGetFuncs(SLogicNode* pNode) {
2,340,623✔
278
  if (QUERY_NODE_LOGIC_PLAN_PARTITION == nodeType(pNode)) {
2,340,623✔
279
    if (!pNode->pParent || QUERY_NODE_LOGIC_PLAN_WINDOW != nodeType(pNode->pParent) ||
160,391!
280
        WINDOW_TYPE_INTERVAL == ((SWindowLogicNode*)pNode->pParent)->winType)
30,595✔
281
      return !scanPathOptHaveNormalCol(((SPartitionLogicNode*)pNode)->pPartitionKeys);
150,445✔
282
    return false;
9,946✔
283
  }
284

285
  if ((QUERY_NODE_LOGIC_PLAN_WINDOW == nodeType(pNode) &&
2,180,232✔
286
       WINDOW_TYPE_INTERVAL == ((SWindowLogicNode*)pNode)->winType)) {
38,416✔
287
    return true;
24,688✔
288
  }
289
  if (QUERY_NODE_LOGIC_PLAN_AGG == nodeType(pNode)) {
2,155,544✔
290
    return !scanPathOptHaveNormalCol(((SAggLogicNode*)pNode)->pGroupKeys);
795,671✔
291
  }
292
  return false;
1,359,873✔
293
}
294

295
static SNodeList* scanPathOptGetAllFuncs(SLogicNode* pNode) {
2,340,619✔
296
  if (!scanPathOptShouldGetFuncs(pNode)) return NULL;
2,340,619✔
297
  switch (nodeType(pNode)) {
822,658!
298
    case QUERY_NODE_LOGIC_PLAN_WINDOW:
24,688✔
299
      return ((SWindowLogicNode*)pNode)->pFuncs;
24,688✔
300
    case QUERY_NODE_LOGIC_PLAN_AGG:
653,169✔
301
      return ((SAggLogicNode*)pNode)->pAggFuncs;
653,169✔
302
    case QUERY_NODE_LOGIC_PLAN_PARTITION:
144,828✔
303
      return ((SPartitionLogicNode*)pNode)->pAggFuncs;
144,828✔
304
    default:
×
305
      break;
×
306
  }
307
  return NULL;
×
308
}
309

310
static bool scanPathOptIsSpecifiedFuncType(const SFunctionNode* pFunc, bool (*typeCheckFn)(int32_t)) {
1,246,990✔
311
  if (!typeCheckFn(pFunc->funcId)) return false;
1,246,990✔
312
  SNode* pPara;
313
  FOREACH(pPara, pFunc->pParameterList) {
1,088,626✔
314
    if (QUERY_NODE_COLUMN != nodeType(pPara) && QUERY_NODE_VALUE != nodeType(pPara)) {
551,016✔
315
      return false;
17,754✔
316
    }
317
  }
318
  return true;
537,610✔
319
}
320

321
static int32_t scanPathOptGetRelatedFuncs(SScanLogicNode* pScan, SNodeList** pSdrFuncs, SNodeList** pDsoFuncs) {
1,170,285✔
322
  SNodeList* pAllFuncs = scanPathOptGetAllFuncs(pScan->node.pParent);
1,170,285✔
323
  SNodeList* pTmpSdrFuncs = NULL;
1,170,363✔
324
  SNodeList* pTmpDsoFuncs = NULL;
1,170,363✔
325
  SNode*     pNode = NULL;
1,170,363✔
326
  bool       otherFunc = false;
1,170,363✔
327
  FOREACH(pNode, pAllFuncs) {
1,707,995✔
328
    SFunctionNode* pFunc = (SFunctionNode*)pNode;
726,644✔
329
    int32_t        code = TSDB_CODE_SUCCESS;
726,644✔
330
    if (scanPathOptIsSpecifiedFuncType(pFunc, fmIsSpecialDataRequiredFunc)) {
726,644✔
331
      SNode* pNew = NULL;
466,445✔
332
      code = nodesCloneNode(pNode, &pNew);
466,445✔
333
      if (TSDB_CODE_SUCCESS == code) {
466,454!
334
        code = nodesListMakeStrictAppend(&pTmpSdrFuncs, pNew);
466,454✔
335
      }
336
    } else if (scanPathOptIsSpecifiedFuncType(pFunc, fmIsDynamicScanOptimizedFunc)) {
260,191!
337
      SNode* pNew = NULL;
×
338
      code = nodesCloneNode(pNode, &pNew);
×
339
      if (TSDB_CODE_SUCCESS == code) {
×
340
        code = nodesListMakeStrictAppend(&pTmpDsoFuncs, pNew);
×
341
      }
342
    } else if (scanPathOptIsSpecifiedFuncType(pFunc, fmIsSkipScanCheckFunc)) {
260,192✔
343
      continue;
71,179✔
344
    } else {
345
      otherFunc = true;
189,020✔
346
      break;
189,020✔
347
    }
348
    if (TSDB_CODE_SUCCESS != code) {
466,453!
349
      nodesDestroyList(pTmpSdrFuncs);
×
350
      nodesDestroyList(pTmpDsoFuncs);
×
351
      return code;
×
352
    }
353
  }
354
  if (otherFunc) {
1,170,371✔
355
    nodesDestroyList(pTmpSdrFuncs);
189,022✔
356
    nodesDestroyList(pTmpDsoFuncs);
189,018✔
357
  } else {
358
    *pSdrFuncs = pTmpSdrFuncs;
981,349✔
359
    *pDsoFuncs = pTmpDsoFuncs;
981,349✔
360
  }
361
  return TSDB_CODE_SUCCESS;
1,170,371✔
362
}
363

364
static int32_t scanPathOptGetScanOrder(SScanLogicNode* pScan, EScanOrder* pScanOrder) {
1,170,337✔
365
  SNodeList* pAllFuncs = scanPathOptGetAllFuncs(pScan->node.pParent);
1,170,337✔
366
  SNode*     pNode = NULL;
1,170,377✔
367
  bool       hasFirst = false;
1,170,377✔
368
  bool       hasLast = false;
1,170,377✔
369
  bool       otherFunc = false;
1,170,377✔
370
  FOREACH(pNode, pAllFuncs) {
2,217,837✔
371
    SFunctionNode* pFunc = (SFunctionNode*)pNode;
1,047,460✔
372
    if (FUNCTION_TYPE_FIRST == pFunc->funcType) {
1,047,460✔
373
      hasFirst = true;
71,308✔
374
    } else if (FUNCTION_TYPE_LAST == pFunc->funcType || FUNCTION_TYPE_LAST_ROW == pFunc->funcType) {
976,152✔
375
      hasLast = true;
210,904✔
376
    } else if (FUNCTION_TYPE_SELECT_VALUE != pFunc->funcType) {
765,248✔
377
      otherFunc = true;
740,114✔
378
    }
379
  }
380
  if (hasFirst && hasLast && !otherFunc) {
1,170,377✔
381
    *pScanOrder = SCAN_ORDER_BOTH;
129✔
382
  } else if (hasLast) {
1,170,248✔
383
    *pScanOrder = SCAN_ORDER_DESC;
91,673✔
384
  } else {
385
    *pScanOrder = SCAN_ORDER_ASC;
1,078,575✔
386
  }
387
  return TSDB_CODE_SUCCESS;
1,170,377✔
388
}
389

390
static int32_t scanPathOptSetOsdInfo(SOsdInfo* pInfo) {
1,170,317✔
391
  int32_t code = scanPathOptGetRelatedFuncs(pInfo->pScan, &pInfo->pSdrFuncs, &pInfo->pDsoFuncs);
1,170,317✔
392
  if (TSDB_CODE_SUCCESS == code) {
1,170,368!
393
    code = scanPathOptGetScanOrder(pInfo->pScan, &pInfo->scanOrder);
1,170,376✔
394
  }
395
  return code;
1,170,370✔
396
}
397

398
static int32_t scanPathOptMatch(SOptimizeContext* pCxt, SLogicNode* pLogicNode, SOsdInfo* pInfo) {
3,685,555✔
399
  pInfo->pScan = (SScanLogicNode*)optFindPossibleNode(pLogicNode, scanPathOptMayBeOptimized, NULL);
3,685,555✔
400
  if (NULL == pInfo->pScan) {
3,685,724✔
401
    return TSDB_CODE_SUCCESS;
2,515,374✔
402
  }
403
  return scanPathOptSetOsdInfo(pInfo);
1,170,350✔
404
}
405

406
static EFuncDataRequired scanPathOptPromoteDataRequired(EFuncDataRequired l, EFuncDataRequired r) {
370,966✔
407
  switch (l) {
370,966!
408
    case FUNC_DATA_REQUIRED_DATA_LOAD:
×
409
      return l;
×
410
    case FUNC_DATA_REQUIRED_SMA_LOAD:
160,121✔
411
      return FUNC_DATA_REQUIRED_DATA_LOAD == r ? r : l;
160,121!
412
    case FUNC_DATA_REQUIRED_NOT_LOAD:
13,964✔
413
      return FUNC_DATA_REQUIRED_FILTEROUT == r ? l : r;
13,964!
414
    default:
196,881✔
415
      break;
196,881✔
416
  }
417
  return r;
196,881✔
418
}
419

420
static int32_t scanPathOptGetDataRequired(SNodeList* pFuncs) {
196,865✔
421
  if (NULL == pFuncs) {
196,865!
422
    return FUNC_DATA_REQUIRED_DATA_LOAD;
×
423
  }
424
  EFuncDataRequired dataRequired = FUNC_DATA_REQUIRED_FILTEROUT;
196,865✔
425
  SNode*            pFunc = NULL;
196,865✔
426
  FOREACH(pFunc, pFuncs) {
567,835✔
427
    dataRequired = scanPathOptPromoteDataRequired(dataRequired, fmFuncDataRequired((SFunctionNode*)pFunc, NULL));
370,954✔
428
  }
429
  return dataRequired;
196,881✔
430
}
431

432
static void scanPathOptSetScanWin(SScanLogicNode* pScan) {
1,170,289✔
433
  SLogicNode* pParent = pScan->node.pParent;
1,170,289✔
434
  if (QUERY_NODE_LOGIC_PLAN_PARTITION == nodeType(pParent) && pParent->pParent &&
1,170,289!
435
      QUERY_NODE_LOGIC_PLAN_WINDOW == nodeType(pParent->pParent)) {
80,196✔
436
    pParent = pParent->pParent;
15,298✔
437
  }
438
  if (QUERY_NODE_LOGIC_PLAN_WINDOW == nodeType(pParent)) {
1,170,289✔
439
    pScan->interval = ((SWindowLogicNode*)pParent)->interval;
34,506✔
440
    pScan->offset = ((SWindowLogicNode*)pParent)->offset;
34,506✔
441
    pScan->sliding = ((SWindowLogicNode*)pParent)->sliding;
34,506✔
442
    pScan->intervalUnit = ((SWindowLogicNode*)pParent)->intervalUnit;
34,506✔
443
    pScan->slidingUnit = ((SWindowLogicNode*)pParent)->slidingUnit;
34,506✔
444
  }
445
}
1,170,289✔
446

447
static void scanPathOptSetScanOrder(EScanOrder scanOrder, SScanLogicNode* pScan) {
1,168,715✔
448
  if (pScan->sortPrimaryKey || pScan->scanSeq[0] > 1 || pScan->scanSeq[1] > 1) {
1,168,715!
449
    return;
733✔
450
  }
451
  pScan->node.outputTsOrder = (SCAN_ORDER_ASC == scanOrder) ? ORDER_ASC : ORDER_DESC;
1,167,982✔
452
  switch (scanOrder) {
1,167,982✔
453
    case SCAN_ORDER_ASC:
1,076,535✔
454
      pScan->scanSeq[0] = 1;
1,076,535✔
455
      pScan->scanSeq[1] = 0;
1,076,535✔
456
      optSetParentOrder(pScan->node.pParent, ORDER_ASC, NULL);
1,076,535✔
457
      break;
1,076,530✔
458
    case SCAN_ORDER_DESC:
91,318✔
459
      pScan->scanSeq[0] = 0;
91,318✔
460
      pScan->scanSeq[1] = 1;
91,318✔
461
      optSetParentOrder(pScan->node.pParent, ORDER_DESC, NULL);
91,318✔
462
      break;
91,313✔
463
    case SCAN_ORDER_BOTH:
128✔
464
      pScan->scanSeq[0] = 1;
128✔
465
      pScan->scanSeq[1] = 1;
128✔
466
      break;
128✔
467
    default:
1✔
468
      break;
1✔
469
  }
470
}
471

472
static void scanPathOptSetGroupOrderScan(SScanLogicNode* pScan) {
1,221,304✔
473
  if (pScan->tableType != TSDB_SUPER_TABLE) return;
1,221,304✔
474

475
  if (pScan->node.pParent && nodeType(pScan->node.pParent) == QUERY_NODE_LOGIC_PLAN_AGG) {
484,612!
476
    SAggLogicNode* pAgg = (SAggLogicNode*)pScan->node.pParent;
165,795✔
477
    bool           withSlimit = pAgg->node.pSlimit != NULL;
165,795✔
478
    if (withSlimit && (isPartTableAgg(pAgg) || isPartTagAgg(pAgg))) {
165,795!
479
      pScan->groupOrderScan = pAgg->node.forceCreateNonBlockingOptr = true;
72✔
480
    }
481
  }
482
}
483

484
static int32_t scanPathOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
3,685,559✔
485
  SOsdInfo info = {.scanOrder = SCAN_ORDER_ASC};
3,685,559✔
486
  int32_t  code = scanPathOptMatch(pCxt, pLogicSubplan->pNode, &info);
3,685,559✔
487
  if (TSDB_CODE_SUCCESS == code && info.pScan) {
3,685,718!
488
    scanPathOptSetScanWin(info.pScan);
1,170,377✔
489
    if (!pCxt->pPlanCxt->streamQuery) {
1,170,301✔
490
      scanPathOptSetScanOrder(info.scanOrder, info.pScan);
1,168,729✔
491
    }
492
    scanPathOptSetGroupOrderScan(info.pScan);
1,170,315✔
493
  }
494
  if (TSDB_CODE_SUCCESS == code && (NULL != info.pDsoFuncs || NULL != info.pSdrFuncs)) {
3,685,703!
495
    if (pCxt->pPlanCxt->streamQuery) {
197,087✔
496
      info.pScan->dataRequired = FUNC_DATA_REQUIRED_DATA_LOAD;  // always load all data for stream query
214✔
497
    } else {
498
      info.pScan->dataRequired = scanPathOptGetDataRequired(info.pSdrFuncs);
196,873✔
499
    }
500

501
    info.pScan->pDynamicScanFuncs = info.pDsoFuncs;
197,096✔
502
  }
503
  if (TSDB_CODE_SUCCESS == code && info.pScan) {
3,685,712!
504
    OPTIMIZE_FLAG_SET_MASK(info.pScan->node.optimizedFlag, OPTIMIZE_FLAG_SCAN_PATH);
1,170,375✔
505
    pCxt->optimized = true;
1,170,375✔
506
  }
507
  nodesDestroyList(info.pSdrFuncs);
3,685,712✔
508
  return code;
3,685,716✔
509
}
510

511

512
static int32_t pushDownCondOptCalcTimeRange(SOptimizeContext* pCxt, SScanLogicNode* pScan, SNode** pPrimaryKeyCond,
152,818✔
513
                                            SNode** pOtherCond) {
514
  int32_t code = TSDB_CODE_SUCCESS;
152,818✔
515
  if (pCxt->pPlanCxt->topicQuery || pCxt->pPlanCxt->streamQuery) {
152,818!
516
    code = nodesMergeNode(pOtherCond, pPrimaryKeyCond);
47✔
517
  } else {
518
    bool isStrict = false;
152,771✔
519
    code = filterGetTimeRange(*pPrimaryKeyCond, &pScan->scanRange, &isStrict);
152,771✔
520
    if (TSDB_CODE_SUCCESS == code) {
152,766!
521
      if (isStrict) {
152,766✔
522
        nodesDestroyNode(*pPrimaryKeyCond);
152,059✔
523
      } else {
524
        code = nodesMergeNode(pOtherCond, pPrimaryKeyCond);
707✔
525
      }
526
      *pPrimaryKeyCond = NULL;
152,785✔
527
    }
528
  }
529
  return code;
152,866✔
530
}
531

532
static int32_t pushDownCondOptRebuildTbanme(SNode** pTagCond) {
57,379✔
533
  int32_t code = TSDB_CODE_SUCCESS;
57,379✔
534
  nodesRewriteExpr(pTagCond, optRebuildTbanme, &code);
57,379✔
535
  return code;
57,379✔
536
}
537

538
static void rewriteDnodeConds(SNode** pCond, SNodeList* pDnodeConds) {
382✔
539
  if (nodeType(*pCond) == QUERY_NODE_LOGIC_CONDITION) {
382✔
540
    SLogicConditionNode* pCondNode = *(SLogicConditionNode**)pCond;
16✔
541
    if (pCondNode->condType == LOGIC_COND_TYPE_AND) {
16!
542
      SNode* pNode = NULL;
16✔
543
      WHERE_EACH(pNode, pCondNode->pParameterList) {
48!
544
        rewriteDnodeConds(&cell->pNode, pDnodeConds);
32✔
545
        if (cell->pNode == NULL) {
32✔
546
          ERASE_NODE(pCondNode->pParameterList);
16✔
547
          continue;
16✔
548
        }
549
        WHERE_NEXT;
16✔
550
      }
551
      if (pCondNode->pParameterList->length == 1) {
16!
552
        nodesCloneNode(pCondNode->pParameterList->pHead->pNode, pCond);
16✔
553
        nodesDestroyList(pCondNode->pParameterList);
16✔
554
      } else if (pCondNode->pParameterList->length == 0) {
×
555
        nodesDestroyNode(*pCond);
×
556
        *pCond = NULL;
×
557
      }
558
    } else {
559
      return;
×
560
    }
561
  } else if (nodeType(*pCond) == QUERY_NODE_OPERATOR) {
366!
562
    SOperatorNode* pOperNode = *(SOperatorNode**)pCond;
366✔
563
    if (pOperNode->opType == OP_TYPE_EQUAL || pOperNode->opType == OP_TYPE_NOT_EQUAL) {
366!
564
      SNode* pLeft = pOperNode->pLeft;
347✔
565
      SNode* pRight = pOperNode->pRight;
347✔
566
      if ((QUERY_NODE_COLUMN == nodeType(pLeft) && strcmp(((SColumnNode*)pLeft)->node.aliasName, "dnode_id") == 0) ||
347!
567
          (QUERY_NODE_COLUMN == nodeType(pRight) && strcmp(((SColumnNode*)pRight)->node.aliasName, "dnode_id") == 0)) {
×
568
        nodesListAppend(pDnodeConds, (SNode*)pOperNode);
347✔
569
        *pCond = NULL;
347✔
570
      }
571
    }
572
  }
573
  return;
382✔
574
}
575

576
static int32_t filterDnodeConds(SOptimizeContext* pCxt, SScanLogicNode* pScan, SNodeList** pDnodeConds) {
531,100✔
577
  if(pScan->node.pConditions == NULL) return TSDB_CODE_SUCCESS;
531,100✔
578
  if(pScan->pVgroupList == NULL) {
330,165✔
579
    return TSDB_CODE_SUCCESS;
4,367✔
580
  } 
581
  if(TSDB_SYSTEM_TABLE != pScan->tableType || strcmp(pScan->tableName.tname, "ins_dnode_variables") != 0) {
325,798✔
582
    return TSDB_CODE_SUCCESS;
325,448✔
583
  }
584
  int32_t code = nodesMakeList(pDnodeConds);
350✔
585
  if (TSDB_CODE_SUCCESS != code) {
350!
586
    return code;
×
587
  }
588
  rewriteDnodeConds(&pScan->node.pConditions, *pDnodeConds);
350✔
589
  return TSDB_CODE_SUCCESS;
350✔
590
}
591

592
static int32_t pushDownDnodeConds(SScanLogicNode* pScan, SNodeList* pDnodeConds) {
531,101✔
593
  int32_t code = TSDB_CODE_SUCCESS;
531,101✔
594
  if (!pDnodeConds || pDnodeConds->length == 0) {
531,101✔
595
    return TSDB_CODE_SUCCESS;
530,754✔
596
  }
597

598
  int32_t dnodeCount = pScan->pVgroupList->numOfVgroups;
347✔
599
  bool*   dnodeDelState = taosMemoryCalloc(dnodeCount + 1, sizeof(bool));
347!
600
  if (NULL == dnodeDelState) {
347!
601
    return terrno;
×
602
  }
603

604
  SNode* pNode = NULL;
347✔
605
  SNode* pNewCond = NULL;
347✔
606
  FOREACH(pNewCond, pDnodeConds) {
694!
607
    if (nodeType(pNewCond) != QUERY_NODE_OPERATOR) {
347!
608
      code = TSDB_CODE_TSC_INTERNAL_ERROR;
×
609
      goto _exit;
×
610
    }
611
    SOperatorNode* pOperNode = (SOperatorNode*)pNewCond;
347✔
612
    int32_t        nodeId = -1;
347✔
613
    EOperatorType  operType = pOperNode->opType;
347✔
614
    SNode*         pLeft = pOperNode->pLeft;
347✔
615
    SNode*         pRight = pOperNode->pRight;
347✔
616
    if ((QUERY_NODE_COLUMN == nodeType(pLeft) && strcmp(((SColumnNode*)pLeft)->node.aliasName, "dnode_id") == 0)) {
347!
617
      if (nodeType(pRight) == QUERY_NODE_VALUE) {
347!
618
        SValueNode* pVal = (SValueNode*)pRight;
347✔
619
        if (IS_SIGNED_NUMERIC_TYPE(pVal->node.resType.type)) {
347!
620
          nodeId = pVal->datum.i;
347✔
621
        }
622
      }
623
    } else if ((QUERY_NODE_COLUMN == nodeType(pRight) &&
×
624
                strcmp(((SColumnNode*)pRight)->node.aliasName, "dnode_id") == 0)) {
×
625
      if (nodeType(pLeft) == QUERY_NODE_VALUE) {
×
626
        SValueNode* pVal = (SValueNode*)pLeft;
×
627
        if (IS_SIGNED_NUMERIC_TYPE(pVal->node.resType.type)) {
×
628
          nodeId = pVal->datum.i;
×
629
        }
630
      }
631
    } else {
632
      code = TSDB_CODE_TSC_INTERNAL_ERROR;
×
633
      goto _exit;
×
634
    }
635
    if (operType == OP_TYPE_EQUAL) {
347!
636
      for (int i = 1; i <= dnodeCount; i++) {
1,348✔
637
        if (i != nodeId) {
1,001✔
638
          dnodeDelState[i] = true;
654✔
639
        }
640
      }
641
    } else {
642
      if (nodeId > 0 && nodeId <= dnodeCount) {
×
643
        dnodeDelState[nodeId] = true;
×
644
      }
645
    }
646
  }
647

648
  int32_t resultCount = 0;
347✔
649
  for (int i = 1; i <= dnodeCount; i++) {
1,348✔
650
    if (!dnodeDelState[i]) {
1,001✔
651
      resultCount++;
347✔
652
    }
653
  }
654

655
  if(resultCount == dnodeCount) goto _exit;
347✔
656
  if(resultCount == 0) {
328!
657
    taosMemoryFree(pScan->pVgroupList);
×
658
    pScan->pVgroupList = NULL;
×
659
    goto _exit;
×
660
  }
661

662
  SVgroupsInfo* pNewVgroupList = (SVgroupsInfo*)taosMemoryMalloc(sizeof(SVgroupsInfo) + sizeof(SVgroupInfo) * resultCount);
328!
663
  if (NULL == pNewVgroupList) {
328!
664
    code = terrno;
×
665
    goto _exit;
×
666
  }
667
  pNewVgroupList->numOfVgroups = 0;
328✔
668
  for (int num = 0; num < dnodeCount; num++) {
1,310✔
669
    SVgroupInfo* pVgInfo = &pScan->pVgroupList->vgroups[num];
982✔
670
    if (!dnodeDelState[pVgInfo->vgId]) {
982✔
671
      pNewVgroupList->vgroups[pNewVgroupList->numOfVgroups] = *pVgInfo;
328✔
672
      pNewVgroupList->numOfVgroups++;
328✔
673
    }
674
  }
675
  taosMemoryFree(pScan->pVgroupList);
328!
676
  pScan->pVgroupList = pNewVgroupList;
328✔
677

678
_exit:
347✔
679
  taosMemoryFree(dnodeDelState);
347!
680
  if(TSDB_CODE_SUCCESS != code) {
347!
681
    taosMemoryFree(pNewVgroupList);
×
682
  }
683
  return code;
347✔
684
}
685

686
static int32_t pdcDealScan(SOptimizeContext* pCxt, SScanLogicNode* pScan) {
3,356,250✔
687
  if (NULL == pScan->node.pConditions ||
3,356,250✔
688
      OPTIMIZE_FLAG_TEST_MASK(pScan->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE)
1,370,417✔
689
      // || TSDB_SYSTEM_TABLE == pScan->tableType
690
     ) {
691
    return TSDB_CODE_SUCCESS;
2,825,109✔
692
  }
693

694
  SNode*  pPrimaryKeyCond = NULL;
531,141✔
695
  SNode*  pOtherCond = NULL;
531,141✔
696
  SNodeList*  pDnodeConds = NULL;
531,141✔
697
  int32_t code = filterPartitionCond(&pScan->node.pConditions, &pPrimaryKeyCond, &pScan->pTagIndexCond,
531,141✔
698
                                     &pScan->pTagCond, &pOtherCond);
699
  if (TSDB_CODE_SUCCESS == code && NULL != pScan->pTagCond) {
531,128✔
700
    code = pushDownCondOptRebuildTbanme(&pScan->pTagCond);
57,379✔
701
  }
702
  if (TSDB_CODE_SUCCESS == code && NULL != pPrimaryKeyCond) {
531,128✔
703
    code = pushDownCondOptCalcTimeRange(pCxt, pScan, &pPrimaryKeyCond, &pOtherCond);
152,850✔
704
  }
705
  if (TSDB_CODE_SUCCESS == code) {
531,139!
706
    pScan->node.pConditions = pOtherCond;
531,143✔
707
  }
708
  code = filterDnodeConds(pCxt, pScan, &pDnodeConds);
531,139✔
709
  if (TSDB_CODE_SUCCESS == code) {
531,120✔
710
    code = pushDownDnodeConds(pScan, pDnodeConds);
531,110✔
711
  }
712
  if(pDnodeConds != NULL) {
531,112✔
713
    nodesDestroyList(pDnodeConds);
350✔
714
    pDnodeConds = NULL;
350✔
715
  }
716

717
  if (TSDB_CODE_SUCCESS == code) {
531,112!
718
    OPTIMIZE_FLAG_SET_MASK(pScan->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE);
531,112✔
719
    pCxt->optimized = true;
531,112✔
720
  } else {
721
    nodesDestroyNode(pPrimaryKeyCond);
×
722
    nodesDestroyNode(pOtherCond);
×
723
  }
724

725
  return code;
531,114✔
726
}
727

728
static bool pdcColBelongThisTable(SNode* pCondCol, SNodeList* pTableCols) {
40,844✔
729
  SNode* pTableCol = NULL;
40,844✔
730
  FOREACH(pTableCol, pTableCols) {
118,987!
731
    if (QUERY_NODE_COLUMN == nodeType(pCondCol) && QUERY_NODE_COLUMN == nodeType(pTableCol)) {
118,559!
732
      SColumnNode* pCondColNode = (SColumnNode*)pCondCol;
118,559✔
733
      SColumnNode* pTblColNode = (SColumnNode*)pTableCol;
118,559✔
734
      if (0 == strcmp(pCondColNode->tableAlias, pTblColNode->tableAlias) &&
118,559✔
735
          0 == strcmp(pCondColNode->colName, pTblColNode->colName)) {
116,654✔
736
        return true;
40,416✔
737
      }
738
    }
739

740
    if (nodesEqualNode(pCondCol, pTableCol)) {
78,143!
741
      return true;
×
742
    }
743
  }
744
  return false;
428✔
745
}
746

747
static bool pdcJoinColInTableColList(SNode* pNode, SNodeList* pTableCols) {
40,844✔
748
  if (QUERY_NODE_COLUMN != nodeType(pNode)) {
40,844!
749
    return false;
×
750
  }
751
  SColumnNode* pCol = (SColumnNode*)pNode;
40,844✔
752
  return pdcColBelongThisTable(pNode, pTableCols);
40,844✔
753
}
754

755
static bool pdcJoinColInTableList(SNode* pCondCol, SSHashObj* pTables) {
2,301,782✔
756
  char* pTableAlias = NULL;
2,301,782✔
757
  if (QUERY_NODE_COLUMN == nodeType(pCondCol)) {
2,301,782✔
758
    SColumnNode* pTableCol = (SColumnNode*)pCondCol;
2,300,488✔
759
    pTableAlias = pTableCol->tableAlias;
2,300,488✔
760
  } else if (QUERY_NODE_VALUE == nodeType(pCondCol)) {
1,294!
761
    SValueNode* pVal = (SValueNode*)pCondCol;
1,294✔
762
    pTableAlias = pVal->node.srcTable;
1,294✔
763
  }
764
  
765
  if (NULL == tSimpleHashGet(pTables, pTableAlias, strlen(pTableAlias))) {
2,301,782✔
766
    return false;
902,000✔
767
  }
768
  return true;
1,399,782✔
769
}
770

771
static EDealRes pdcJoinIsCrossTableCond(SNode* pNode, void* pContext) {
2,330,440✔
772
  SCpdIsMultiTableCondCxt* pCxt = pContext;
2,330,440✔
773
  if (QUERY_NODE_COLUMN == nodeType(pNode)) {
2,330,440✔
774
    if (pdcJoinColInTableList(pNode, pCxt->pLeftTbls)) {
898,298✔
775
      pCxt->havaLeftCol = true;
504,437✔
776
    } 
777

778
    if (pdcJoinColInTableList(pNode, pCxt->pRightTbls)) {
898,298✔
779
      pCxt->haveRightCol = true;
393,863✔
780
    }
781
    return pCxt->havaLeftCol && pCxt->haveRightCol ? DEAL_RES_END : DEAL_RES_CONTINUE;
898,298✔
782
  }
783
  return DEAL_RES_CONTINUE;
1,432,142✔
784
}
785

786
static ECondAction pdcJoinGetCondAction(SJoinLogicNode* pJoin, SSHashObj* pLeftTbls, SSHashObj* pRightTbls,
475,665✔
787
                                        SNode* pNode, bool whereCond) {
788
  EJoinType               t = pJoin->joinType;
475,665✔
789
  EJoinSubType            s = pJoin->subType;
475,665✔
790
  SCpdIsMultiTableCondCxt cxt = {
475,665✔
791
      .pLeftTbls = pLeftTbls, .pRightTbls = pRightTbls, .havaLeftCol = false, .haveRightCol = false};
792
  nodesWalkExpr(pNode, pdcJoinIsCrossTableCond, &cxt);
475,665✔
793

794
  if (cxt.havaLeftCol) {
475,665✔
795
    if (cxt.haveRightCol) {
373,691✔
796
      if (whereCond && gJoinWhereOpt[t][s].pushDownFlag & PUSH_DOWN_ON_COND) {
260,722✔
797
        return COND_ACTION_PUSH_JOIN;
60,192✔
798
      }
799
      return COND_ACTION_STAY;
200,530✔
800
    }
801
    if ((whereCond && gJoinWhereOpt[t][s].pushDownFlag & PUSH_DOWN_LEFT_FLT) ||
112,969✔
802
        (!whereCond && gJoinOnOpt[t][s].pushDownFlag & PUSH_DOWN_LEFT_FLT)) {
1,049✔
803
      return COND_ACTION_PUSH_LEFT_CHILD;
112,581✔
804
    }
805
    return COND_ACTION_STAY;
388✔
806
  }
807

808
  if (cxt.haveRightCol) {
101,974✔
809
    if ((whereCond && gJoinWhereOpt[t][s].pushDownFlag & PUSH_DOWN_RIGHT_FLT) ||
101,876✔
810
        (!whereCond && gJoinOnOpt[t][s].pushDownFlag & PUSH_DOWN_RIGHT_FLT)) {
934✔
811
      return COND_ACTION_PUSH_RIGHT_CHILD;
101,477✔
812
    }
813
    return COND_ACTION_STAY;
399✔
814
  }
815

816
  return COND_ACTION_STAY;
98✔
817
}
818

819
static int32_t pdcJoinSplitLogicCond(SJoinLogicNode* pJoin, SNode** pSrcCond, SNode** pOnCond, SNode** pLeftChildCond,
158,091✔
820
                                     SNode** pRightChildCond, bool whereCond) {
821
  SLogicConditionNode* pLogicCond = (SLogicConditionNode*)*pSrcCond;
158,091✔
822
  if (LOGIC_COND_TYPE_AND != pLogicCond->condType) {
158,091✔
823
    if (whereCond) {
201!
824
      return TSDB_CODE_SUCCESS;
201✔
825
    }
826
    return TSDB_CODE_PLAN_NOT_SUPPORT_JOIN_COND;
×
827
  }
828

829
  int32_t    code = TSDB_CODE_SUCCESS;
157,890✔
830
  SSHashObj* pLeftTables = NULL;
157,890✔
831
  SSHashObj* pRightTables = NULL;
157,890✔
832
  code = collectTableAliasFromNodes(nodesListGetNode(pJoin->node.pChildren, 0), &pLeftTables);
157,890✔
833
  if (TSDB_CODE_SUCCESS != code) {
157,890!
834
    return code;
×
835
  }
836
  code = collectTableAliasFromNodes(nodesListGetNode(pJoin->node.pChildren, 1), &pRightTables);
157,890✔
837
  if (TSDB_CODE_SUCCESS != code) {
157,890!
838
    tSimpleHashCleanup(pLeftTables);
×
839
    return code;
×
840
  }
841

842
  SNodeList* pOnConds = NULL;
157,890✔
843
  SNodeList* pLeftChildConds = NULL;
157,890✔
844
  SNodeList* pRightChildConds = NULL;
157,890✔
845
  SNodeList* pRemainConds = NULL;
157,890✔
846
  SNode*     pCond = NULL;
157,890✔
847
  FOREACH(pCond, pLogicCond->pParameterList) {
559,420!
848
    ECondAction condAction = pdcJoinGetCondAction(pJoin, pLeftTables, pRightTables, pCond, whereCond);
401,530✔
849
    SNode*      pNew = NULL;
401,530✔
850
    code = nodesCloneNode(pCond, &pNew);
401,530✔
851
    if (TSDB_CODE_SUCCESS != code) {
401,530!
852
      break;
×
853
    }
854
    if (COND_ACTION_PUSH_JOIN == condAction && NULL != pOnCond) {
401,530!
855
      code = nodesListMakeAppend(&pOnConds, pNew);
59,973✔
856
    } else if (COND_ACTION_PUSH_LEFT_CHILD == condAction) {
341,557✔
857
      code = nodesListMakeAppend(&pLeftChildConds, pNew);
105,899✔
858
    } else if (COND_ACTION_PUSH_RIGHT_CHILD == condAction) {
235,658✔
859
      code = nodesListMakeAppend(&pRightChildConds, pNew);
97,849✔
860
    } else {
861
      code = nodesListMakeAppend(&pRemainConds, pNew);
137,809✔
862
    }
863
    if (TSDB_CODE_SUCCESS != code) {
401,530!
864
      break;
×
865
    }
866
  }
867

868
  tSimpleHashCleanup(pLeftTables);
157,890✔
869
  tSimpleHashCleanup(pRightTables);
157,890✔
870

871
  SNode* pTempOnCond = NULL;
157,890✔
872
  SNode* pTempLeftChildCond = NULL;
157,890✔
873
  SNode* pTempRightChildCond = NULL;
157,890✔
874
  SNode* pTempRemainCond = NULL;
157,890✔
875
  if (TSDB_CODE_SUCCESS == code) {
157,890!
876
    code = nodesMergeConds(&pTempOnCond, &pOnConds);
157,890✔
877
  }
878
  if (TSDB_CODE_SUCCESS == code) {
157,890!
879
    code = nodesMergeConds(&pTempLeftChildCond, &pLeftChildConds);
157,890✔
880
  }
881
  if (TSDB_CODE_SUCCESS == code) {
157,890!
882
    code = nodesMergeConds(&pTempRightChildCond, &pRightChildConds);
157,890✔
883
  }
884
  if (TSDB_CODE_SUCCESS == code) {
157,890!
885
    code = nodesMergeConds(&pTempRemainCond, &pRemainConds);
157,890✔
886
  }
887

888
  if (TSDB_CODE_SUCCESS == code) {
157,890!
889
    if (pOnCond) {
157,890✔
890
      *pOnCond = pTempOnCond;
97,516✔
891
    }
892
    *pLeftChildCond = pTempLeftChildCond;
157,890✔
893
    *pRightChildCond = pTempRightChildCond;
157,890✔
894
    nodesDestroyNode(*pSrcCond);
157,890✔
895
    *pSrcCond = pTempRemainCond;
157,890✔
896
  } else {
897
    nodesDestroyList(pOnConds);
×
898
    nodesDestroyList(pLeftChildConds);
×
899
    nodesDestroyList(pRightChildConds);
×
900
    nodesDestroyList(pRemainConds);
×
901
    nodesDestroyNode(pTempOnCond);
×
902
    nodesDestroyNode(pTempLeftChildCond);
×
903
    nodesDestroyNode(pTempRightChildCond);
×
904
    nodesDestroyNode(pTempRemainCond);
×
905
  }
906

907
  return code;
157,890✔
908
}
909

910
static int32_t pdcJoinSplitOpCond(SJoinLogicNode* pJoin, SNode** pSrcCond, SNode** pOnCond, SNode** pLeftChildCond,
74,135✔
911
                                  SNode** pRightChildCond, bool whereCond) {
912
  SSHashObj* pLeftTables = NULL;
74,135✔
913
  SSHashObj* pRightTables = NULL;
74,135✔
914
  int32_t    code = collectTableAliasFromNodes(nodesListGetNode(pJoin->node.pChildren, 0), &pLeftTables);
74,135✔
915
  if (TSDB_CODE_SUCCESS != code) {
74,135!
916
    return code;
×
917
  }
918
  code = collectTableAliasFromNodes(nodesListGetNode(pJoin->node.pChildren, 1), &pRightTables);
74,135✔
919
  if (TSDB_CODE_SUCCESS != code) {
74,135!
920
    tSimpleHashCleanup(pLeftTables);
×
921
    return code;
×
922
  }
923

924
  ECondAction condAction = pdcJoinGetCondAction(pJoin, pLeftTables, pRightTables, *pSrcCond, whereCond);
74,135✔
925

926
  tSimpleHashCleanup(pLeftTables);
74,135✔
927
  tSimpleHashCleanup(pRightTables);
74,135✔
928

929
  if (COND_ACTION_STAY == condAction || (COND_ACTION_PUSH_JOIN == condAction && NULL == pOnCond)) {
74,135!
930
    return TSDB_CODE_SUCCESS;
63,606✔
931
  }
932

933
  if (COND_ACTION_PUSH_JOIN == condAction) {
10,529✔
934
    *pOnCond = *pSrcCond;
219✔
935
  } else if (COND_ACTION_PUSH_LEFT_CHILD == condAction) {
10,310✔
936
    *pLeftChildCond = *pSrcCond;
6,682✔
937
  } else if (COND_ACTION_PUSH_RIGHT_CHILD == condAction) {
3,628!
938
    *pRightChildCond = *pSrcCond;
3,628✔
939
  }
940
  *pSrcCond = NULL;
10,529✔
941
  return TSDB_CODE_SUCCESS;
10,529✔
942
}
943

944
static int32_t pdcJoinSplitCond(SJoinLogicNode* pJoin, SNode** pSrcCond, SNode** pOnCond, SNode** pLeftChildCond,
232,226✔
945
                                SNode** pRightChildCond, bool whereCond) {
946
  if (QUERY_NODE_LOGIC_CONDITION == nodeType(*pSrcCond)) {
232,226✔
947
    return pdcJoinSplitLogicCond(pJoin, pSrcCond, pOnCond, pLeftChildCond, pRightChildCond, whereCond);
158,091✔
948
  } else {
949
    return pdcJoinSplitOpCond(pJoin, pSrcCond, pOnCond, pLeftChildCond, pRightChildCond, whereCond);
74,135✔
950
  }
951
}
952

953
static int32_t pdcJoinPushDownOnCond(SOptimizeContext* pCxt, SJoinLogicNode* pJoin, SNode** pCond) {
56,556✔
954
  return nodesMergeNode(&pJoin->pFullOnCond, pCond);
56,556✔
955
}
956

957
static int32_t pdcPushDownCondToChild(SOptimizeContext* pCxt, SLogicNode* pChild, SNode** pCond) {
211,982✔
958
  return nodesMergeNode(&pChild->pConditions, pCond);
211,982✔
959
}
960

961
static bool pdcJoinIsPrim(SNode* pNode, SSHashObj* pTables, bool constAsPrim, bool* constPrimGot) {
585,299✔
962
  if (QUERY_NODE_COLUMN != nodeType(pNode) && QUERY_NODE_FUNCTION != nodeType(pNode) && (!constAsPrim || QUERY_NODE_VALUE != nodeType(pNode))) {
585,299✔
963
    return false;
2,512✔
964
  }
965

966
  if (QUERY_NODE_VALUE == nodeType(pNode)) {
582,787✔
967
    SValueNode* pVal = (SValueNode*)pNode;
1,294✔
968
    if (TSDB_DATA_TYPE_NULL != pVal->node.resType.type && !pVal->isNull) {
1,294!
969
      if (pdcJoinColInTableList(pNode, pTables)) {
1,294✔
970
        *constPrimGot = true;
1,124✔
971
        return true;
1,124✔
972
      }
973
    }
974

975
    return false;
170✔
976
  }
977

978
  if (QUERY_NODE_FUNCTION == nodeType(pNode)) {
581,493✔
979
    SFunctionNode* pFunc = (SFunctionNode*)pNode;
2,808✔
980
    if (FUNCTION_TYPE_TIMETRUNCATE != pFunc->funcType) {
2,808✔
981
      return false;
72✔
982
    }
983
    SListCell* pCell = nodesListGetCell(pFunc->pParameterList, 0);
2,736✔
984
    if (NULL == pCell || NULL == pCell->pNode || QUERY_NODE_COLUMN != nodeType(pCell->pNode)) {
2,736!
985
      return false;
4✔
986
    }
987
    pNode = pCell->pNode;
2,732✔
988
  }
989

990
  SColumnNode* pCol = (SColumnNode*)pNode;
581,417✔
991
  if (PRIMARYKEY_TIMESTAMP_COL_ID != pCol->colId || TSDB_SYSTEM_TABLE == pCol->tableType) {
581,417!
992
    return false;
80,234✔
993
  }
994
  return pdcJoinColInTableList(pNode, pTables);
501,183✔
995
}
996

997
static bool pdcJoinIsPrimEqualCond(SJoinLogicNode* pJoin, SNode* pCond, bool constAsPrim) {
349,031✔
998
  if (QUERY_NODE_OPERATOR != nodeType(pCond)) {
349,031✔
999
    return false;
56,743✔
1000
  }
1001

1002
  SOperatorNode* pOper = (SOperatorNode*)pCond;
292,288✔
1003
  if (OP_TYPE_EQUAL != pOper->opType) {
292,288✔
1004
    if (JOIN_STYPE_ASOF != pJoin->subType) {
1,367✔
1005
      return false;
857✔
1006
    }
1007
    if (OP_TYPE_GREATER_THAN != pOper->opType && OP_TYPE_GREATER_EQUAL != pOper->opType &&
510✔
1008
        OP_TYPE_LOWER_THAN != pOper->opType && OP_TYPE_LOWER_EQUAL != pOper->opType) {
184!
1009
      return false;
×
1010
    }
1011
  }
1012

1013
  SSHashObj* pLeftTables = NULL;
291,431✔
1014
  SSHashObj* pRightTables = NULL;
291,431✔
1015
  int32_t    code = collectTableAliasFromNodes(nodesListGetNode(pJoin->node.pChildren, 0), &pLeftTables);
291,431✔
1016
  if (TSDB_CODE_SUCCESS != code) {
291,431!
1017
    return code;
×
1018
  }
1019
  code = collectTableAliasFromNodes(nodesListGetNode(pJoin->node.pChildren, 1), &pRightTables);
291,431✔
1020
  if (TSDB_CODE_SUCCESS != code) {
291,431!
1021
    tSimpleHashCleanup(pLeftTables);
×
1022
    return code;
×
1023
  }
1024

1025
  bool res = false, constGot = false;
291,431✔
1026
  if (pdcJoinIsPrim(pOper->pLeft, pLeftTables, constAsPrim, &pJoin->leftConstPrimGot)) {
291,431✔
1027
    res = pdcJoinIsPrim(pOper->pRight, pRightTables, constAsPrim, &pJoin->rightConstPrimGot);
248,057✔
1028
  } else if (pdcJoinIsPrim(pOper->pLeft, pRightTables, constAsPrim, &pJoin->rightConstPrimGot)) {
43,374✔
1029
    res = pdcJoinIsPrim(pOper->pRight, pLeftTables, constAsPrim, &pJoin->leftConstPrimGot);
2,437✔
1030
    if (pJoin->rightConstPrimGot || pJoin->leftConstPrimGot) {
2,437✔
1031
      TSWAP(pOper->pLeft, pOper->pRight);
474✔
1032
    }
1033
  }
1034

1035
  if (constAsPrim && ((pJoin->leftNoOrderedSubQuery && !pJoin->leftConstPrimGot) || (pJoin->rightNoOrderedSubQuery && !pJoin->rightConstPrimGot))) {
291,431✔
1036
    res = false;
146✔
1037
  }
1038

1039
  tSimpleHashCleanup(pLeftTables);
291,431✔
1040
  tSimpleHashCleanup(pRightTables);
291,431✔
1041

1042
  return res;
291,431✔
1043
}
1044

1045
static bool pdcJoinHasPrimEqualCond(SJoinLogicNode* pJoin, SNode* pCond, bool* errCond) {
167,567✔
1046
  if (QUERY_NODE_LOGIC_CONDITION == nodeType(pCond)) {
167,567✔
1047
    SLogicConditionNode* pLogicCond = (SLogicConditionNode*)pCond;
21,522✔
1048
    if (LOGIC_COND_TYPE_AND != pLogicCond->condType) {
21,522✔
1049
      if (errCond) {
242✔
1050
        *errCond = true;
122✔
1051
      }
1052
      return false;
242✔
1053
    }
1054
    bool   hasPrimaryKeyEqualCond = false;
21,280✔
1055
    SNode* pCond = NULL;
21,280✔
1056
    FOREACH(pCond, pLogicCond->pParameterList) {
41,777!
1057
      if (pdcJoinHasPrimEqualCond(pJoin, pCond, NULL)) {
41,381✔
1058
        hasPrimaryKeyEqualCond = true;
20,884✔
1059
        break;
20,884✔
1060
      }
1061
    }
1062
    return hasPrimaryKeyEqualCond;
21,280✔
1063
  } else {
1064
    return pdcJoinIsPrimEqualCond(pJoin, pCond, false);
146,045✔
1065
  }
1066
}
1067

1068
static int32_t pdcJoinSplitPrimInLogicCond(SJoinLogicNode* pJoin, SNode** ppInput, SNode** ppPrimEqCond, SNode** ppOnCond, bool constAsPrim) {
60,277✔
1069
  SLogicConditionNode* pLogicCond = (SLogicConditionNode*)(*ppInput);
60,277✔
1070

1071
  int32_t    code = TSDB_CODE_SUCCESS;
60,277✔
1072
  SNodeList* pOnConds = NULL;
60,277✔
1073
  SNode*     pCond = NULL;
60,277✔
1074
  WHERE_EACH(pCond, pLogicCond->pParameterList) {
198,035!
1075
    SNode* pNew = NULL;
137,758✔
1076
    if (pdcJoinIsPrimEqualCond(pJoin, pCond, constAsPrim) && (NULL == *ppPrimEqCond) && (!constAsPrim || pJoin->leftConstPrimGot || pJoin->rightConstPrimGot)) {
137,758!
1077
      code = nodesCloneNode(pCond, &pNew);
60,218✔
1078
      if (TSDB_CODE_SUCCESS != code) break;
60,218!
1079
      *ppPrimEqCond = pNew;
60,218✔
1080
      ERASE_NODE(pLogicCond->pParameterList);
60,218✔
1081
    } else {
1082
      code = nodesCloneNode(pCond, &pNew);
77,540✔
1083
      if (TSDB_CODE_SUCCESS != code) break;
77,540!
1084
      code = nodesListMakeAppend(&pOnConds, pNew);
77,540✔
1085
      if (TSDB_CODE_SUCCESS != code) break;
77,540!
1086
      WHERE_NEXT;
77,540✔
1087
    }
1088
  }
1089

1090
  SNode* pTempOnCond = NULL;
60,277✔
1091
  if (TSDB_CODE_SUCCESS == code) {
60,277!
1092
    code = nodesMergeConds(&pTempOnCond, &pOnConds);
60,277✔
1093
  }
1094

1095
  if (TSDB_CODE_SUCCESS == code) {
60,277!
1096
    nodesDestroyNode(*ppInput);
60,277✔
1097
    *ppInput = NULL;
60,277✔
1098

1099
    if (NULL != *ppPrimEqCond) {
60,277✔
1100
      *ppOnCond = pTempOnCond;
60,218✔
1101
      return TSDB_CODE_SUCCESS;
60,218✔
1102
    }
1103
    
1104
    nodesDestroyNode(pTempOnCond);
59✔
1105
    if (!constAsPrim) {
59!
1106
      planError("no primary key equal cond found, condListNum:%d", pLogicCond->pParameterList->length);
×
1107
      return TSDB_CODE_PLAN_INTERNAL_ERROR;
×
1108
    }
1109
  } else {
1110
    nodesDestroyList(pOnConds);
×
1111
    nodesDestroyNode(pTempOnCond);
×
1112
    return code;
×
1113
  }
1114

1115
  return code;
59✔
1116
}
1117

1118
static int32_t pdcJoinSplitPrimEqCond(SOptimizeContext* pCxt, SJoinLogicNode* pJoin) {
124,066✔
1119
  int32_t code = TSDB_CODE_SUCCESS;
124,066✔
1120
  SNode*  pPrimKeyEqCond = NULL;
124,066✔
1121
  SNode*  pJoinOnCond = NULL;
124,066✔
1122

1123
  if (QUERY_NODE_LOGIC_CONDITION == nodeType(pJoin->pFullOnCond) &&
124,066✔
1124
      LOGIC_COND_TYPE_AND == ((SLogicConditionNode*)(pJoin->pFullOnCond))->condType) {
59,914!
1125
    code = pdcJoinSplitPrimInLogicCond(pJoin, &pJoin->pFullOnCond, &pPrimKeyEqCond, &pJoinOnCond, false);
59,914✔
1126
  } else if (pdcJoinIsPrimEqualCond(pJoin, pJoin->pFullOnCond, false)) {
64,152!
1127
    pPrimKeyEqCond = pJoin->pFullOnCond;
64,152✔
1128
    pJoinOnCond = NULL;
64,152✔
1129
  } else {
1130
    return TSDB_CODE_SUCCESS;
×
1131
  }
1132

1133
  if (TSDB_CODE_SUCCESS == code) {
124,066!
1134
    pJoin->pPrimKeyEqCond = pPrimKeyEqCond;
124,066✔
1135
    pJoin->pFullOnCond = pJoinOnCond;
124,066✔
1136
  } else {
1137
    nodesDestroyNode(pPrimKeyEqCond);
×
1138
    nodesDestroyNode(pJoinOnCond);
×
1139
  }
1140
  return code;
124,066✔
1141
}
1142

1143
static int32_t pdcJoinIsEqualOnCond(SJoinLogicNode* pJoin, SNode* pCond, bool* allTags, bool* pRes) {
79,849✔
1144
  *pRes = false;
79,849✔
1145
  int32_t code = 0;
79,849✔
1146
  if (QUERY_NODE_OPERATOR != nodeType(pCond)) {
79,849✔
1147
    return code;
58,601✔
1148
  }
1149
  SOperatorNode* pOper = (SOperatorNode*)pCond;
21,248✔
1150
  if ((QUERY_NODE_COLUMN != nodeType(pOper->pLeft) &&
21,248✔
1151
       !(QUERY_NODE_OPERATOR == nodeType(pOper->pLeft) &&
253✔
1152
         OP_TYPE_JSON_GET_VALUE == ((SOperatorNode*)pOper->pLeft)->opType)) ||
68✔
1153
      NULL == pOper->pRight ||
21,027✔
1154
      (QUERY_NODE_COLUMN != nodeType(pOper->pRight) &&
20,977✔
1155
       !(QUERY_NODE_OPERATOR == nodeType(pOper->pRight) &&
353✔
1156
         OP_TYPE_JSON_GET_VALUE == ((SOperatorNode*)pOper->pRight)->opType))) {
62✔
1157
    return code;
592✔
1158
  }
1159

1160
  if (OP_TYPE_EQUAL != pOper->opType) {
20,656✔
1161
    return code;
205✔
1162
  }
1163

1164
  if ((QUERY_NODE_OPERATOR == nodeType(pOper->pLeft) || QUERY_NODE_OPERATOR == nodeType(pOper->pRight)) &&
20,451!
1165
      !(IS_ASOF_JOIN(pJoin->subType) || IS_WINDOW_JOIN(pJoin->subType))) {
32✔
1166
    return code;
24✔
1167
  }
1168

1169
  SColumnNode* pLeft = (SColumnNode*)(pOper->pLeft);
20,427✔
1170
  SColumnNode* pRight = (SColumnNode*)(pOper->pRight);
20,427✔
1171

1172
  if (QUERY_NODE_OPERATOR == nodeType(pOper->pLeft)) {
20,427✔
1173
    pLeft = (SColumnNode*)((SOperatorNode*)pOper->pLeft)->pLeft;
8✔
1174
  }
1175

1176
  if (QUERY_NODE_OPERATOR == nodeType(pOper->pRight)) {
20,427✔
1177
    pRight = (SColumnNode*)((SOperatorNode*)pOper->pRight)->pLeft;
8✔
1178
  }
1179

1180
  *allTags = (COLUMN_TYPE_TAG == pLeft->colType) && (COLUMN_TYPE_TAG == pRight->colType);
20,427!
1181

1182
  if (pLeft->node.resType.type != pRight->node.resType.type ||
20,427✔
1183
      pLeft->node.resType.bytes != pRight->node.resType.bytes) {
20,339✔
1184
    return code;
213✔
1185
  }
1186
  SNodeList* pLeftCols = ((SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 0))->pTargets;
20,214✔
1187
  SNodeList* pRightCols = ((SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 1))->pTargets;
20,214✔
1188
  bool       isEqual = false;
20,214✔
1189
  if (pdcJoinColInTableColList((SNode*)pLeft, pLeftCols)) {
20,214✔
1190
    isEqual = pdcJoinColInTableColList((SNode*)pRight, pRightCols);
19,794✔
1191
    if (isEqual) {
19,794✔
1192
      SNode* pNewLeft = NULL;
19,790✔
1193
      code = nodesCloneNode(pOper->pLeft, &pNewLeft);
19,790✔
1194
      if (TSDB_CODE_SUCCESS != code) return code;
19,790!
1195
      SNode* pNewRight = NULL;
19,790✔
1196
      code = nodesCloneNode(pOper->pRight, &pNewRight);
19,790✔
1197
      if (TSDB_CODE_SUCCESS != code) {
19,790!
1198
        nodesDestroyNode(pNewLeft);
×
1199
        return code;
×
1200
      }
1201
      code = nodesListMakeStrictAppend(&pJoin->pLeftEqNodes, pNewLeft);
19,790✔
1202
      if (TSDB_CODE_SUCCESS != code) {
19,790!
1203
        nodesDestroyNode(pNewRight);
×
1204
        return code;
×
1205
      }
1206
      code = nodesListMakeStrictAppend(&pJoin->pRightEqNodes, pNewRight);
19,790✔
1207
    }
1208
  } else if (pdcJoinColInTableColList((SNode*)pLeft, pRightCols)) {
420✔
1209
    isEqual = pdcJoinColInTableColList((SNode*)pRight, pLeftCols);
416✔
1210
    if (isEqual) {
416!
1211
      SNode* pNewLeft = NULL;
416✔
1212
      code = nodesCloneNode(pOper->pLeft, &pNewLeft);
416✔
1213
      if (TSDB_CODE_SUCCESS != code) return code;
416!
1214
      SNode* pNewRight = NULL;
416✔
1215
      code = nodesCloneNode(pOper->pRight, &pNewRight);
416✔
1216
      if (TSDB_CODE_SUCCESS != code) {
416!
1217
        nodesDestroyNode(pNewLeft);
×
1218
        return code;
×
1219
      }
1220
      code = nodesListMakeStrictAppend(&pJoin->pLeftEqNodes, pNewRight);
416✔
1221
      if (TSDB_CODE_SUCCESS != code) {
416!
1222
        nodesDestroyNode(pNewLeft);
×
1223
        return code;
×
1224
      }
1225
      code = nodesListMakeStrictAppend(&pJoin->pRightEqNodes, pNewLeft);
416✔
1226
    }
1227
  }
1228
  if (TSDB_CODE_SUCCESS == code) {
20,214!
1229
    *pRes = isEqual;
20,214✔
1230
  }
1231
  return code;
20,214✔
1232
}
1233

1234
static int32_t pdcJoinPartLogicEqualOnCond(SJoinLogicNode* pJoin) {
19,127✔
1235
  SLogicConditionNode* pLogicCond = (SLogicConditionNode*)(pJoin->pFullOnCond);
19,127✔
1236

1237
  int32_t    code = TSDB_CODE_SUCCESS;
19,127✔
1238
  SNodeList* pColEqOnConds = NULL;
19,127✔
1239
  SNodeList* pTagEqOnConds = NULL;
19,127✔
1240
  SNodeList* pTagOnConds = NULL;
19,127✔
1241
  SNodeList* pColOnConds = NULL;
19,127✔
1242
  SNode*     pCond = NULL;
19,127✔
1243
  bool       allTags = false;
19,127✔
1244
  FOREACH(pCond, pLogicCond->pParameterList) {
57,444!
1245
    allTags = false;
38,317✔
1246
    bool   eqOnCond = false;
38,317✔
1247
    SNode* pNew = NULL;
38,317✔
1248
    code = pdcJoinIsEqualOnCond(pJoin, pCond, &allTags, &eqOnCond);
38,317✔
1249
    if (TSDB_CODE_SUCCESS != code) break;
38,317!
1250
    code = nodesCloneNode(pCond, &pNew);
38,317✔
1251
    if (TSDB_CODE_SUCCESS != code) break;
38,317!
1252

1253
    if (eqOnCond) {
38,317✔
1254
      if (allTags) {
17,209✔
1255
        code = nodesListMakeStrictAppend(&pTagEqOnConds, pNew);
17,035✔
1256
      } else {
1257
        code = nodesListMakeStrictAppend(&pColEqOnConds, pNew);
174✔
1258
        pJoin->allEqTags = false;
174✔
1259
      }
1260
    } else if (allTags) {
21,108!
1261
      code = nodesListMakeStrictAppend(&pTagOnConds, pNew);
×
1262
    } else {
1263
      code = nodesListMakeStrictAppend(&pColOnConds, pNew);
21,108✔
1264
      pJoin->allEqTags = false;
21,108✔
1265
    }
1266

1267
    if (code) {
38,317!
1268
      break;
×
1269
    }
1270
  }
1271

1272
  SNode* pTempTagEqCond = NULL;
19,127✔
1273
  SNode* pTempColEqCond = NULL;
19,127✔
1274
  SNode* pTempTagOnCond = NULL;
19,127✔
1275
  SNode* pTempColOnCond = NULL;
19,127✔
1276
  if (TSDB_CODE_SUCCESS == code) {
19,127!
1277
    code = nodesMergeConds(&pTempColEqCond, &pColEqOnConds);
19,127✔
1278
  }
1279
  if (TSDB_CODE_SUCCESS == code) {
19,127!
1280
    code = nodesMergeConds(&pTempTagEqCond, &pTagEqOnConds);
19,127✔
1281
  }
1282
  if (TSDB_CODE_SUCCESS == code) {
19,127!
1283
    code = nodesMergeConds(&pTempTagOnCond, &pTagOnConds);
19,127✔
1284
  }
1285
  if (TSDB_CODE_SUCCESS == code) {
19,127!
1286
    code = nodesMergeConds(&pTempColOnCond, &pColOnConds);
19,127✔
1287
  }
1288

1289
  if (TSDB_CODE_SUCCESS == code) {
19,127!
1290
    pJoin->pColEqCond = pTempColEqCond;
19,127✔
1291
    pJoin->pColOnCond = pTempColOnCond;
19,127✔
1292
    pJoin->pTagEqCond = pTempTagEqCond;
19,127✔
1293
    pJoin->pTagOnCond = pTempTagOnCond;
19,127✔
1294
  } else {
1295
    nodesDestroyList(pColEqOnConds);
×
1296
    nodesDestroyList(pTagEqOnConds);
×
1297
    nodesDestroyList(pColOnConds);
×
1298
    nodesDestroyList(pTagOnConds);
×
1299
    nodesDestroyNode(pTempTagEqCond);
×
1300
    nodesDestroyNode(pTempColEqCond);
×
1301
    nodesDestroyNode(pTempTagOnCond);
×
1302
    nodesDestroyNode(pTempColOnCond);
×
1303
  }
1304

1305
  return code;
19,127✔
1306
}
1307

1308
static int32_t pdcJoinPartEqualOnCond(SOptimizeContext* pCxt, SJoinLogicNode* pJoin) {
126,221✔
1309
  if (NULL == pJoin->pFullOnCond) {
126,221✔
1310
    pJoin->pColEqCond = NULL;
65,562✔
1311
    pJoin->pTagEqCond = NULL;
65,562✔
1312
    return TSDB_CODE_SUCCESS;
65,562✔
1313
  }
1314

1315
  pJoin->allEqTags = true;
60,659✔
1316

1317
  if (QUERY_NODE_LOGIC_CONDITION == nodeType(pJoin->pFullOnCond) &&
60,659✔
1318
      LOGIC_COND_TYPE_AND == ((SLogicConditionNode*)(pJoin->pFullOnCond))->condType) {
56,911✔
1319
    return pdcJoinPartLogicEqualOnCond(pJoin);
19,127✔
1320
  }
1321

1322
  bool    allTags = false;
41,532✔
1323
  bool    eqOnCond = false;
41,532✔
1324
  int32_t code = pdcJoinIsEqualOnCond(pJoin, pJoin->pFullOnCond, &allTags, &eqOnCond);
41,532✔
1325
  if (TSDB_CODE_SUCCESS != code) {
41,532!
1326
    return code;
×
1327
  }
1328
  SNode* pNew = NULL;
41,532✔
1329
  code = nodesCloneNode(pJoin->pFullOnCond, &pNew);
41,532✔
1330
  if (TSDB_CODE_SUCCESS != code) {
41,532!
1331
    return code;
×
1332
  }
1333
  if (eqOnCond) {
41,532✔
1334
    if (allTags) {
2,997✔
1335
      pJoin->pTagEqCond = pNew;
2,495✔
1336
    } else {
1337
      pJoin->pColEqCond = pNew;
502✔
1338
      pJoin->allEqTags = false;
502✔
1339
    }
1340
  } else if (allTags) {
38,535✔
1341
    pJoin->pTagOnCond = pNew;
12✔
1342
  } else {
1343
    pJoin->pColOnCond = pNew;
38,523✔
1344
    pJoin->allEqTags = false;
38,523✔
1345
  }
1346

1347
  return TSDB_CODE_SUCCESS;
41,532✔
1348
}
1349

1350
static EDealRes pdcJoinCollectCondCol(SNode* pNode, void* pContext) {
1,083✔
1351
  SCpdCollectTableColCxt* pCxt = pContext;
1,083✔
1352
  if (QUERY_NODE_COLUMN == nodeType(pNode)) {
1,083✔
1353
    if (pdcJoinColInTableList(pNode, pCxt->pTables)) {
722✔
1354
      SColumnNode* pCol = (SColumnNode*)pNode;
237✔
1355
      char         name[TSDB_TABLE_NAME_LEN + TSDB_COL_NAME_LEN];
1356
      int32_t      len = 0;
237✔
1357
      if ('\0' == pCol->tableAlias[0]) {
237!
1358
        len = tsnprintf(name, sizeof(name), "%s", pCol->colName);
×
1359
      } else {
1360
        len = tsnprintf(name, sizeof(name), "%s.%s", pCol->tableAlias, pCol->colName);
237✔
1361
      }
1362
      if (NULL == taosHashGet(pCxt->pColHash, name, len)) {
237!
1363
        pCxt->errCode = taosHashPut(pCxt->pColHash, name, len, NULL, 0);
237✔
1364
        if (TSDB_CODE_SUCCESS == pCxt->errCode) {
237!
1365
          SNode* pNew = NULL;
237✔
1366
          pCxt->errCode = nodesCloneNode(pNode, &pNew);
237✔
1367
          if (TSDB_CODE_SUCCESS == pCxt->errCode) {
237!
1368
            pCxt->errCode = nodesListStrictAppend(pCxt->pResCols, pNew);
237✔
1369
          }
1370
        }
1371
      }
1372
    }
1373
  }
1374

1375
  return (TSDB_CODE_SUCCESS == pCxt->errCode ? DEAL_RES_CONTINUE : DEAL_RES_ERROR);
1,083!
1376
}
1377

1378
static int32_t pdcJoinCollectColsFromParent(SJoinLogicNode* pJoin, SSHashObj* pTables, SNodeList* pCondCols) {
246✔
1379
  SCpdCollectTableColCxt cxt = {
492✔
1380
      .errCode = TSDB_CODE_SUCCESS,
1381
      .pTables = pTables,
1382
      .pResCols = pCondCols,
1383
      .pColHash = taosHashInit(128, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_NO_LOCK)};
246✔
1384
  if (NULL == cxt.pColHash) {
246!
1385
    return terrno;
×
1386
  }
1387

1388
  nodesWalkExpr(pJoin->pPrimKeyEqCond, pdcJoinCollectCondCol, &cxt);
246✔
1389
  nodesWalkExpr(pJoin->node.pConditions, pdcJoinCollectCondCol, &cxt);
246✔
1390
  if (TSDB_CODE_SUCCESS == cxt.errCode) {
246!
1391
    nodesWalkExpr(pJoin->pFullOnCond, pdcJoinCollectCondCol, &cxt);
246✔
1392
  }
1393

1394
  taosHashCleanup(cxt.pColHash);
246✔
1395
  return cxt.errCode;
246✔
1396
}
1397

1398
static int32_t pdcJoinAddParentOnColsToTarget(SOptimizeContext* pCxt, SJoinLogicNode* pJoin) {
126,184✔
1399
  if (NULL == pJoin->node.pParent || QUERY_NODE_LOGIC_PLAN_JOIN != nodeType(pJoin->node.pParent)) {
126,184!
1400
    return TSDB_CODE_SUCCESS;
126,094✔
1401
  }
1402

1403
  SNodeList* pTargets = NULL;
90✔
1404
  int32_t    code = TSDB_CODE_SUCCESS;
90✔
1405
  SNodeList* pCondCols = NULL;
90✔
1406
  code = nodesMakeList(&pCondCols);
90✔
1407
  if (NULL == pCondCols) {
90!
1408
    return code;
×
1409
  }
1410

1411
  SSHashObj* pTables = NULL;
90✔
1412
  code = collectTableAliasFromNodes(nodesListGetNode(pJoin->node.pChildren, 0), &pTables);
90✔
1413
  if (TSDB_CODE_SUCCESS != code) {
90!
1414
    return code;
×
1415
  }
1416
  code = collectTableAliasFromNodes(nodesListGetNode(pJoin->node.pChildren, 1), &pTables);
90✔
1417
  if (TSDB_CODE_SUCCESS != code) {
90!
1418
    tSimpleHashCleanup(pTables);
×
1419
    return code;
×
1420
  }
1421

1422
  SJoinLogicNode* pTmp = (SJoinLogicNode*)pJoin->node.pParent;
90✔
1423
  do {
1424
    code = pdcJoinCollectColsFromParent(pTmp, pTables, pCondCols);
246✔
1425
    if (TSDB_CODE_SUCCESS != code) {
246!
1426
      break;
×
1427
    }
1428
    if (NULL == pTmp->node.pParent || QUERY_NODE_LOGIC_PLAN_JOIN != nodeType(pTmp->node.pParent)) {
246!
1429
      break;
1430
    }
1431
    pTmp = (SJoinLogicNode*)pTmp->node.pParent;
156✔
1432
  } while (true);
1433

1434
  tSimpleHashCleanup(pTables);
90✔
1435

1436
  if (TSDB_CODE_SUCCESS == code) {
90!
1437
    code = createColumnByRewriteExprs(pCondCols, &pTargets);
90✔
1438
  }
1439

1440
  nodesDestroyList(pCondCols);
90✔
1441

1442
  if (TSDB_CODE_SUCCESS == code) {
90!
1443
    SNode* pNode = NULL;
90✔
1444
    FOREACH(pNode, pTargets) {
327!
1445
      SNode* pTmp = NULL;
237✔
1446
      bool   found = false;
237✔
1447
      FOREACH(pTmp, pJoin->node.pTargets) {
2,683!
1448
        if (nodesEqualNode(pTmp, pNode)) {
2,649✔
1449
          found = true;
203✔
1450
          break;
203✔
1451
        }
1452
      }
1453
      if (!found) {
237✔
1454
        SNode* pNew = NULL;
34✔
1455
        code = nodesCloneNode(pNode, &pNew);
34✔
1456
        if (TSDB_CODE_SUCCESS == code) {
34!
1457
          code = nodesListStrictAppend(pJoin->node.pTargets, pNew);
34✔
1458
        }
1459
        if (TSDB_CODE_SUCCESS != code) {
34!
1460
          break;
×
1461
        }
1462
      }
1463
    }
1464
  }
1465

1466
  nodesDestroyList(pTargets);
90✔
1467

1468
  return code;
90✔
1469
}
1470

1471
static int32_t pdcJoinAddPreFilterColsToTarget(SOptimizeContext* pCxt, SJoinLogicNode* pJoin) {
×
1472
  if (NULL == pJoin->pFullOnCond) {
×
1473
    return TSDB_CODE_SUCCESS;
×
1474
  }
1475

1476
  int32_t    code = TSDB_CODE_SUCCESS;
×
1477
  SNodeList* pCondCols = NULL;
×
1478
  code = nodesMakeList(&pCondCols);
×
1479
  SNodeList* pTargets = NULL;
×
1480
  if (TSDB_CODE_SUCCESS == code) {
×
1481
    code = nodesCollectColumnsFromNode(pJoin->pColOnCond, NULL, COLLECT_COL_TYPE_ALL, &pCondCols);
×
1482
  }
1483
  if (TSDB_CODE_SUCCESS == code) {
×
1484
    code = nodesCollectColumnsFromNode(pJoin->pTagOnCond, NULL, COLLECT_COL_TYPE_ALL, &pCondCols);
×
1485
  }
1486
  if (TSDB_CODE_SUCCESS == code) {
×
1487
    code = createColumnByRewriteExprs(pCondCols, &pTargets);
×
1488
  }
1489

1490
  nodesDestroyList(pCondCols);
×
1491

1492
  if (TSDB_CODE_SUCCESS == code) {
×
1493
    SNode* pNode = NULL;
×
1494
    FOREACH(pNode, pTargets) {
×
1495
      SNode* pTmp = NULL;
×
1496
      bool   found = false;
×
1497
      FOREACH(pTmp, pJoin->node.pTargets) {
×
1498
        if (nodesEqualNode(pTmp, pNode)) {
×
1499
          found = true;
×
1500
          break;
×
1501
        }
1502
      }
1503
      if (!found) {
×
1504
        SNode* pNew = NULL;
×
1505
        code = nodesCloneNode(pNode, &pNew);
×
1506
        if (TSDB_CODE_SUCCESS == code) {
×
1507
          code = nodesListStrictAppend(pJoin->node.pTargets, pNew);
×
1508
        }
1509
        if (TSDB_CODE_SUCCESS != code) {
×
1510
          break;
×
1511
        }
1512
      }
1513
    }
1514
  }
1515

1516
  nodesDestroyList(pTargets);
×
1517

1518
  return code;
×
1519
}
1520

1521
static int32_t pdcJoinAddFilterColsToTarget(SOptimizeContext* pCxt, SJoinLogicNode* pJoin) {
126,184✔
1522
  if (NULL == pJoin->node.pConditions && NULL == pJoin->pFullOnCond) {
126,184✔
1523
    return TSDB_CODE_SUCCESS;
65,405✔
1524
  }
1525

1526
  int32_t    code = TSDB_CODE_SUCCESS;
60,779✔
1527
  SNodeList* pCondCols = NULL;
60,779✔
1528
  code = nodesMakeList(&pCondCols);
60,779✔
1529
  SNodeList* pTargets = NULL;
60,779✔
1530
  if (NULL == pCondCols) {
60,779!
1531
    return code;
×
1532
  }
1533

1534
  if (NULL != pJoin->node.pConditions) {
60,779✔
1535
    code = nodesCollectColumnsFromNode(pJoin->node.pConditions, NULL, COLLECT_COL_TYPE_ALL, &pCondCols);
769✔
1536
  }
1537
  if (TSDB_CODE_SUCCESS == code && NULL != pJoin->pFullOnCond) {
60,779!
1538
    code = nodesCollectColumnsFromNode(pJoin->pFullOnCond, NULL, COLLECT_COL_TYPE_ALL, &pCondCols);
60,097✔
1539
  }
1540
  if (TSDB_CODE_SUCCESS == code) {
60,779!
1541
    code = createColumnByRewriteExprs(pCondCols, &pTargets);
60,779✔
1542
  }
1543

1544
  nodesDestroyList(pCondCols);
60,779✔
1545

1546
  if (TSDB_CODE_SUCCESS == code) {
60,779!
1547
    SNode* pNode = NULL;
60,779✔
1548
    FOREACH(pNode, pTargets) {
219,825!
1549
      SNode* pTmp = NULL;
159,046✔
1550
      bool   found = false;
159,046✔
1551
      FOREACH(pTmp, pJoin->node.pTargets) {
795,614!
1552
        if (nodesEqualNode(pTmp, pNode)) {
642,549✔
1553
          found = true;
5,981✔
1554
          break;
5,981✔
1555
        }
1556
      }
1557
      if (!found) {
159,046✔
1558
        SNode* pNew = NULL;
153,065✔
1559
        code = nodesCloneNode(pNode, &pNew);
153,065✔
1560
        if (TSDB_CODE_SUCCESS == code) {
153,065!
1561
          code = nodesListStrictAppend(pJoin->node.pTargets, pNew);
153,065✔
1562
        }
1563
        if (TSDB_CODE_SUCCESS != code) {
153,065!
1564
          break;
×
1565
        }
1566
      }
1567
    }
1568
  }
1569

1570
  nodesDestroyList(pTargets);
60,779✔
1571

1572
  return code;
60,779✔
1573
}
1574

1575
static int32_t pdcJoinSplitConstPrimEqCond(SOptimizeContext* pCxt, SJoinLogicNode* pJoin, SNode** ppCond) {
1,439✔
1576
  int32_t code = TSDB_CODE_SUCCESS;
1,439✔
1577
  SNode*  pPrimKeyEqCond = NULL;
1,439✔
1578
  SNode*  pJoinOnCond = NULL;
1,439✔
1579

1580
  if (QUERY_NODE_LOGIC_CONDITION == nodeType(*ppCond) &&
1,439✔
1581
      LOGIC_COND_TYPE_AND == ((SLogicConditionNode*)*ppCond)->condType) {
363!
1582
    code = pdcJoinSplitPrimInLogicCond(pJoin, ppCond, &pPrimKeyEqCond, &pJoinOnCond, true);
363✔
1583
  } else if (pdcJoinIsPrimEqualCond(pJoin, *ppCond, true) && (pJoin->leftConstPrimGot || pJoin->rightConstPrimGot)) {
1,076!
1584
    pPrimKeyEqCond = *ppCond;
694✔
1585
    pJoinOnCond = NULL;
694✔
1586
  } else {
1587
    return TSDB_CODE_SUCCESS;
382✔
1588
  }
1589

1590
  if (TSDB_CODE_SUCCESS == code) {
1,057!
1591
    pJoin->pPrimKeyEqCond = pPrimKeyEqCond;
1,057✔
1592
    *ppCond = pJoinOnCond;
1,057✔
1593
    if (pJoin->pPrimKeyEqCond && (pJoin->rightConstPrimGot || pJoin->leftConstPrimGot)) {
1,057!
1594
      code = scalarConvertOpValueNodeTs((SOperatorNode*)pJoin->pPrimKeyEqCond);
998✔
1595
    }    
1596
  } else {
1597
    nodesDestroyNode(pPrimKeyEqCond);
×
1598
    nodesDestroyNode(pJoinOnCond);
×
1599
  }
1600
  
1601
  return code;
1,057✔
1602
}
1603

1604

1605

1606
static int32_t pdcJoinCheckAllCond(SOptimizeContext* pCxt, SJoinLogicNode* pJoin) {
126,855✔
1607
  if (NULL == pJoin->pFullOnCond) {
126,855✔
1608
    if (IS_WINDOW_JOIN(pJoin->subType) || IS_ASOF_JOIN(pJoin->subType)) {
832✔
1609
      return TSDB_CODE_SUCCESS;
716✔
1610
    }
1611

1612
    if (NULL == pJoin->node.pConditions) {
116✔
1613
      return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PLAN_NOT_SUPPORT_CROSS_JOIN);
15✔
1614
    }
1615

1616
    if (!IS_INNER_NONE_JOIN(pJoin->joinType, pJoin->subType)) {
101!
1617
      return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PLAN_NOT_SUPPORT_CROSS_JOIN);
×
1618
    }
1619
  }
1620

1621
  SNode** ppCond = pJoin->pFullOnCond ? &pJoin->pFullOnCond : &pJoin->node.pConditions;
126,124✔
1622
  bool   errCond = false;
126,124✔
1623
  bool   primCondGot = pdcJoinHasPrimEqualCond(pJoin, *ppCond, &errCond);
126,124✔
1624
  if (!primCondGot) {
126,124✔
1625
    if (errCond && !(IS_INNER_NONE_JOIN(pJoin->joinType, pJoin->subType) && NULL != pJoin->pFullOnCond &&
1,918!
1626
                     NULL != pJoin->node.pConditions)) {
9!
1627
      return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PLAN_NOT_SUPPORT_JOIN_COND);
118✔
1628
    }
1629

1630
    if (IS_INNER_NONE_JOIN(pJoin->joinType, pJoin->subType) && NULL != pJoin->pFullOnCond &&
1,800!
1631
        NULL != pJoin->node.pConditions) {
637✔
1632
      primCondGot = pdcJoinHasPrimEqualCond(pJoin, pJoin->node.pConditions, &errCond);  
62✔
1633
      if (primCondGot) {
62!
1634
        return TSDB_CODE_SUCCESS;
×
1635
      }
1636
      if (errCond) {
62✔
1637
        return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PLAN_NOT_SUPPORT_JOIN_COND);
4✔
1638
      }
1639
    }
1640

1641
    if (IS_WINDOW_JOIN(pJoin->subType) || IS_ASOF_JOIN(pJoin->subType)) {
1,796✔
1642
      return TSDB_CODE_SUCCESS;
441✔
1643
    }
1644
  } 
1645

1646
  if (pJoin->leftNoOrderedSubQuery || pJoin->rightNoOrderedSubQuery || !primCondGot) {
125,561✔
1647
    pJoin->noPrimKeyEqCond = true;
1,439✔
1648
    int32_t code = pdcJoinSplitConstPrimEqCond(pCxt, pJoin, ppCond);
1,439✔
1649
    if (code || (pJoin->pPrimKeyEqCond)) {
1,439!
1650
      return code;
998✔
1651
    }
1652
    
1653
    if (IS_INNER_NONE_JOIN(pJoin->joinType, pJoin->subType) && NULL != pJoin->pFullOnCond &&
441!
1654
        NULL != pJoin->node.pConditions) {
114!
1655
      code = pdcJoinSplitConstPrimEqCond(pCxt, pJoin, &pJoin->node.pConditions);
×
1656
      if (code || pJoin->pPrimKeyEqCond) {
×
1657
        return code;
×
1658
      }
1659
    }
1660

1661
    return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PAR_NOT_SUPPORT_JOIN,
441✔
1662
                                     "Join requires valid time series input and primary timestamp equal condition");
1663
  }
1664

1665
  if (IS_ASOF_JOIN(pJoin->subType)) {
124,122✔
1666
    nodesDestroyNode(pJoin->addPrimEqCond);
513✔
1667
    pJoin->addPrimEqCond = NULL;
513✔
1668
  }
1669

1670
  if (IS_WINDOW_JOIN(pJoin->subType)) {
124,122✔
1671
    return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PLAN_NOT_SUPPORT_JOIN_COND);
56✔
1672
  }
1673

1674
  return TSDB_CODE_SUCCESS;
124,066✔
1675
}
1676

1677
static int32_t pdcJoinHandleGrpJoinCond(SOptimizeContext* pCxt, SJoinLogicNode* pJoin) {
126,221✔
1678
  switch (pJoin->subType) {
126,221✔
1679
    case JOIN_STYPE_ASOF:
1,670✔
1680
    case JOIN_STYPE_WIN:
1681
      if (NULL != pJoin->pColOnCond || NULL != pJoin->pTagOnCond) {
1,670!
1682
        return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PLAN_NOT_SUPPORT_JOIN_COND);
23✔
1683
      }
1684
      nodesDestroyNode(pJoin->pColEqCond);
1,647✔
1685
      pJoin->pColEqCond = NULL;
1,647✔
1686
      nodesDestroyNode(pJoin->pTagEqCond);
1,647✔
1687
      pJoin->pTagEqCond = NULL;
1,647✔
1688
      nodesDestroyNode(pJoin->pFullOnCond);
1,647✔
1689
      pJoin->pFullOnCond = NULL;
1,647✔
1690
      if (!pJoin->allEqTags) {
1,647✔
1691
        SNode* pNode = NULL;
1,395✔
1692
        FOREACH(pNode, pJoin->pLeftEqNodes) {
1,738✔
1693
          SColumnNode* pCol = (SColumnNode*)pNode;
357✔
1694
          if (COLUMN_TYPE_TAG != pCol->colType && PRIMARYKEY_TIMESTAMP_COL_ID == pCol->colId) {
357✔
1695
            return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen,
14✔
1696
                                       TSDB_CODE_PLAN_NOT_SUPPORT_JOIN_COND);
1697
          }
1698
        }
1699
        FOREACH(pNode, pJoin->pRightEqNodes) {
1,724✔
1700
          SColumnNode* pCol = (SColumnNode*)pNode;
343✔
1701
          if (COLUMN_TYPE_TAG != pCol->colType && PRIMARYKEY_TIMESTAMP_COL_ID == pCol->colId) {
343!
1702
            return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen,
×
1703
                                       TSDB_CODE_PLAN_NOT_SUPPORT_JOIN_COND);
1704
          }
1705
        }
1706
      }
1707
      break;
1,633✔
1708
    default:
124,551✔
1709
      break;
124,551✔
1710
  }
1711

1712
  return TSDB_CODE_SUCCESS;
126,184✔
1713
}
1714

1715
static EDealRes pdcCheckTableCondType(SNode* pNode, void* pContext) {
2,176✔
1716
  SCpdIsMultiTableCondCxt* pCxt = pContext;
2,176✔
1717
  switch (nodeType(pNode)) {
2,176✔
1718
    case QUERY_NODE_COLUMN: {
1,123✔
1719
      if (pdcJoinColInTableList(pNode, pCxt->pLeftTbls)) {
1,123✔
1720
        pCxt->havaLeftCol = true;
535✔
1721
      } else if (pdcJoinColInTableList(pNode, pCxt->pRightTbls)) {
588!
1722
        pCxt->haveRightCol = true;
588✔
1723
      }
1724

1725
      break;
1,123✔
1726
    }
1727
    case QUERY_NODE_OPERATOR: {
750✔
1728
      SOperatorNode* pOp = (SOperatorNode*)pNode;
750✔
1729
      if (OP_TYPE_IS_NULL == pOp->opType) {
750✔
1730
        pCxt->condIsNull = true;
90✔
1731
      }
1732
      break;
750✔
1733
    }
1734
    default:
303✔
1735
      break;
303✔
1736
  }
1737

1738
  return DEAL_RES_CONTINUE;
2,176✔
1739
}
1740

1741
static int32_t pdcJoinGetOpTableCondTypes(SNode* pCond, SSHashObj* pLeftTables, SSHashObj* pRightTables,
691✔
1742
                                          bool* tableCondTypes) {
1743
  SCpdIsMultiTableCondCxt cxt = {.pLeftTbls = pLeftTables,
691✔
1744
                                 .pRightTbls = pRightTables,
1745
                                 .havaLeftCol = false,
1746
                                 .haveRightCol = false,
1747
                                 .condIsNull = false};
1748
  nodesWalkExpr(pCond, pdcCheckTableCondType, &cxt);
691✔
1749

1750
  if (cxt.havaLeftCol) {
691✔
1751
    if (cxt.haveRightCol) {
535✔
1752
      if (cxt.condIsNull) {
432✔
1753
        tableCondTypes[1] = true;
17✔
1754
        tableCondTypes[3] = true;
17✔
1755
      } else {
1756
        tableCondTypes[0] = true;
415✔
1757
        tableCondTypes[2] = true;
415✔
1758
      }
1759
      return TSDB_CODE_SUCCESS;
432✔
1760
    }
1761

1762
    if (cxt.condIsNull) {
103✔
1763
      tableCondTypes[1] = true;
42✔
1764
    } else {
1765
      tableCondTypes[0] = true;
61✔
1766
    }
1767

1768
    return TSDB_CODE_SUCCESS;
103✔
1769
  }
1770

1771
  if (cxt.haveRightCol) {
156!
1772
    if (cxt.condIsNull) {
156✔
1773
      tableCondTypes[3] = true;
31✔
1774
    } else {
1775
      tableCondTypes[2] = true;
125✔
1776
    }
1777
  }
1778

1779
  return TSDB_CODE_SUCCESS;
156✔
1780
}
1781

1782
static int32_t pdcJoinGetLogicTableCondTypes(SNode* pCond, SSHashObj* pLeftTables, SSHashObj* pRightTables,
136✔
1783
                                             bool* tableCondTypes) {
1784
  SLogicConditionNode* pLogicCond = (SLogicConditionNode*)pCond;
136✔
1785
  int32_t              code = 0;
136✔
1786
  SNode*               pSCond = NULL;
136✔
1787
  FOREACH(pSCond, pLogicCond->pParameterList) {
562!
1788
    if (QUERY_NODE_LOGIC_CONDITION == nodeType(pSCond)) {
426✔
1789
      code = pdcJoinGetLogicTableCondTypes(pSCond, pLeftTables, pRightTables, tableCondTypes);
18✔
1790
    } else {
1791
      code = pdcJoinGetOpTableCondTypes(pSCond, pLeftTables, pRightTables, tableCondTypes);
408✔
1792
    }
1793

1794
    if (TSDB_CODE_SUCCESS != code) {
426!
1795
      break;
×
1796
    }
1797
  }
1798

1799
  return code;
136✔
1800
}
1801

1802
static int32_t pdcGetTableCondTypes(SNode* pCond, SSHashObj* pLeftTables, SSHashObj* pRightTables,
401✔
1803
                                    bool* tableCondTypes) {
1804
  if (QUERY_NODE_LOGIC_CONDITION == nodeType(pCond)) {
401✔
1805
    return pdcJoinGetLogicTableCondTypes(pCond, pLeftTables, pRightTables, tableCondTypes);
118✔
1806
  } else {
1807
    return pdcJoinGetOpTableCondTypes(pCond, pLeftTables, pRightTables, tableCondTypes);
283✔
1808
  }
1809
}
1810

1811
static int32_t pdcRewriteTypeBasedOnConds(SOptimizeContext* pCxt, SJoinLogicNode* pJoin) {
1,092✔
1812
  if (JOIN_TYPE_INNER == pJoin->joinType || JOIN_STYPE_OUTER != pJoin->subType) {
1,092✔
1813
    return TSDB_CODE_SUCCESS;
691✔
1814
  }
1815

1816
  SSHashObj* pLeftTables = NULL;
401✔
1817
  SSHashObj* pRightTables = NULL;
401✔
1818
  int32_t    code = collectTableAliasFromNodes(nodesListGetNode(pJoin->node.pChildren, 0), &pLeftTables);
401✔
1819
  if (TSDB_CODE_SUCCESS != code) {
401!
1820
    return code;
×
1821
  }
1822

1823
  code = collectTableAliasFromNodes(nodesListGetNode(pJoin->node.pChildren, 1), &pRightTables);
401✔
1824
  if (TSDB_CODE_SUCCESS != code) {
401!
1825
    tSimpleHashCleanup(pLeftTables);
×
1826
    return code;
×
1827
  }
1828

1829
  bool tableCondTypes[4] = {0};
401✔
1830
  code = pdcGetTableCondTypes(pJoin->node.pConditions, pLeftTables, pRightTables, tableCondTypes);
401✔
1831
  tSimpleHashCleanup(pLeftTables);
401✔
1832
  tSimpleHashCleanup(pRightTables);
401✔
1833

1834
  if (TSDB_CODE_SUCCESS != code) return code;
401!
1835

1836
  switch (pJoin->joinType) {
401!
1837
    case JOIN_TYPE_LEFT:
288✔
1838
      if (tableCondTypes[2] && !tableCondTypes[3]) {
288✔
1839
        pJoin->joinType = JOIN_TYPE_INNER;
257✔
1840
        pJoin->subType = JOIN_STYPE_NONE;
257✔
1841
      }
1842
      break;
288✔
1843
    case JOIN_TYPE_RIGHT:
52✔
1844
      if (tableCondTypes[0] && !tableCondTypes[1]) {
52✔
1845
        pJoin->joinType = JOIN_TYPE_INNER;
13✔
1846
        pJoin->subType = JOIN_STYPE_NONE;
13✔
1847
      }
1848
      break;
52✔
1849
    case JOIN_TYPE_FULL:
61✔
1850
      if (tableCondTypes[0] && !tableCondTypes[1]) {
61✔
1851
        if (tableCondTypes[2] && !tableCondTypes[3]) {
36!
1852
          pJoin->joinType = JOIN_TYPE_INNER;
30✔
1853
          pJoin->subType = JOIN_STYPE_NONE;
30✔
1854
        } else {
1855
          pJoin->joinType = JOIN_TYPE_LEFT;
6✔
1856
        }
1857
      } else if (tableCondTypes[2] && !tableCondTypes[3]) {
25!
1858
        pJoin->joinType = JOIN_TYPE_RIGHT;
17✔
1859
      }
1860
      break;
61✔
1861
    default:
×
1862
      break;
×
1863
  }
1864

1865
  return code;
401✔
1866
}
1867

1868
static EDealRes pdcCheckTableResType(SNode* pNode, void* pContext) {
274✔
1869
  SCpdIsMultiTableResCxt* pCxt = pContext;
274✔
1870
  switch (nodeType(pNode)) {
274!
1871
    case QUERY_NODE_COLUMN: {
188✔
1872
      if (pdcJoinColInTableList(pNode, pCxt->pLeftTbls)) {
188✔
1873
        pCxt->haveLeftCol = true;
100✔
1874
      } else if (pdcJoinColInTableList(pNode, pCxt->pRightTbls)) {
88!
1875
        pCxt->haveRightCol = true;
88✔
1876
      }
1877
      break;
188✔
1878
    }
1879
    case QUERY_NODE_VALUE:
2✔
1880
    case QUERY_NODE_GROUPING_SET:
1881
      break;
2✔
1882
    case QUERY_NODE_FUNCTION: {
84✔
1883
      SFunctionNode*         pFunc = (SFunctionNode*)pNode;
84✔
1884
      SCpdIsMultiTableResCxt cxt = {.pLeftTbls = pCxt->pLeftTbls,
84✔
1885
                                    .pRightTbls = pCxt->pRightTbls,
84✔
1886
                                    .haveLeftCol = false,
1887
                                    .haveRightCol = false,
1888
                                    .leftColNonNull = true,
1889
                                    .rightColNonNull = true};
1890

1891
      nodesWalkExprs(pFunc->pParameterList, pdcCheckTableResType, &cxt);
84✔
1892
      if (!cxt.leftColNonNull) {
84!
1893
        pCxt->leftColNonNull = false;
×
1894
      }
1895
      if (!cxt.rightColNonNull) {
84!
1896
        pCxt->rightColNonNull = false;
×
1897
      }
1898
      if (cxt.leftColOp) {
84!
1899
        pCxt->leftColOp = true;
×
1900
      }
1901
      if (cxt.rightColOp) {
84!
1902
        pCxt->rightColOp = true;
×
1903
      }
1904
      if (!cxt.haveLeftCol && !cxt.haveRightCol) {
84✔
1905
        pCxt->leftColNonNull = false;
2✔
1906
        pCxt->rightColNonNull = false;
2✔
1907
        return DEAL_RES_END;
2✔
1908
      } else if (!fmIsIgnoreNullFunc(pFunc->funcId)) {
82!
1909
        if (cxt.haveLeftCol) {
×
1910
          pCxt->leftColNonNull = false;
×
1911
        }
1912
        if (cxt.haveRightCol) {
×
1913
          pCxt->rightColNonNull = false;
×
1914
        }
1915
      } else {
1916
        if (cxt.haveLeftCol) {
82✔
1917
          pCxt->leftColOp = true;
46✔
1918
        } else if (cxt.haveRightCol) {
36!
1919
          pCxt->rightColOp = true;
36✔
1920
        }
1921
      }
1922
      if (!pCxt->leftColNonNull && !pCxt->rightColNonNull) {
82!
1923
        return DEAL_RES_END;
×
1924
      }
1925
      break;
82✔
1926
    }
1927
    default:
×
1928
      pCxt->leftColNonNull = false;
×
1929
      pCxt->rightColNonNull = false;
×
1930
      return DEAL_RES_END;
×
1931
  }
1932

1933
  return DEAL_RES_CONTINUE;
272✔
1934
}
1935

1936
static int32_t pdcRewriteTypeBasedOnJoinRes(SOptimizeContext* pCxt, SJoinLogicNode* pJoin) {
126,586✔
1937
  if (JOIN_TYPE_INNER == pJoin->joinType || JOIN_STYPE_OUTER != pJoin->subType) {
126,586✔
1938
    return TSDB_CODE_SUCCESS;
125,142✔
1939
  }
1940

1941
  int32_t    code = 0;
1,444✔
1942
  SSHashObj* pLeftTables = NULL;
1,444✔
1943
  SSHashObj* pRightTables = NULL;
1,444✔
1944
  code = collectTableAliasFromNodes(nodesListGetNode(pJoin->node.pChildren, 0), &pLeftTables);
1,444✔
1945
  if (TSDB_CODE_SUCCESS != code) {
1,444!
1946
    return code;
×
1947
  }
1948
  code = collectTableAliasFromNodes(nodesListGetNode(pJoin->node.pChildren, 1), &pRightTables);
1,444✔
1949
  if (TSDB_CODE_SUCCESS != code) {
1,444!
1950
    tSimpleHashCleanup(pLeftTables);
×
1951
    return code;
×
1952
  }
1953

1954
  SLogicNode* pParent = pJoin->node.pParent;
1,444✔
1955
  bool        tableResNonNull[2] = {true, true};
1,444✔
1956
  bool        tableResOp[2] = {false, false};
1,444✔
1957
  if (QUERY_NODE_LOGIC_PLAN_AGG == nodeType(pParent)) {
1,444✔
1958
    SAggLogicNode* pAgg = (SAggLogicNode*)pParent;
70✔
1959
    if (NULL != pAgg->pGroupKeys) {
70✔
1960
      tableResNonNull[0] = false;
2✔
1961
      tableResNonNull[1] = false;
2✔
1962
    } else {
1963
      SCpdIsMultiTableResCxt cxt = {.pLeftTbls = pLeftTables,
68✔
1964
                                    .pRightTbls = pRightTables,
1965
                                    .haveLeftCol = false,
1966
                                    .haveRightCol = false,
1967
                                    .leftColNonNull = true,
1968
                                    .rightColNonNull = true,
1969
                                    .leftColOp = false,
1970
                                    .rightColOp = false};
1971

1972
      nodesWalkExprs(pAgg->pAggFuncs, pdcCheckTableResType, &cxt);
68✔
1973
      if (!cxt.leftColNonNull) {
68✔
1974
        tableResNonNull[0] = false;
2✔
1975
      }
1976
      if (!cxt.rightColNonNull) {
68✔
1977
        tableResNonNull[1] = false;
2✔
1978
      }
1979
      if (cxt.leftColOp) {
68✔
1980
        tableResOp[0] = true;
38✔
1981
      }
1982
      if (cxt.rightColOp) {
68✔
1983
        tableResOp[1] = true;
32✔
1984
      }
1985
    }
1986
  } else {
1987
    tableResNonNull[0] = false;
1,374✔
1988
    tableResNonNull[1] = false;
1,374✔
1989
  }
1990

1991
  tSimpleHashCleanup(pLeftTables);
1,444✔
1992
  tSimpleHashCleanup(pRightTables);
1,444✔
1993

1994
  switch (pJoin->joinType) {
1,444!
1995
    case JOIN_TYPE_LEFT:
690✔
1996
      if (tableResNonNull[1] && !tableResOp[0]) {
690✔
1997
        pJoin->joinType = JOIN_TYPE_INNER;
18✔
1998
        pJoin->subType = JOIN_STYPE_NONE;
18✔
1999
      }
2000
      break;
690✔
2001
    case JOIN_TYPE_RIGHT:
411✔
2002
      if (tableResNonNull[0] && !tableResOp[1]) {
411✔
2003
        pJoin->joinType = JOIN_TYPE_INNER;
18✔
2004
        pJoin->subType = JOIN_STYPE_NONE;
18✔
2005
      }
2006
      break;
411✔
2007
    case JOIN_TYPE_FULL:
343✔
2008
      if (tableResNonNull[1] && !tableResOp[0]) {
343✔
2009
        if (tableResNonNull[0] && !tableResOp[1]) {
2!
2010
          pJoin->joinType = JOIN_TYPE_INNER;
×
2011
          pJoin->subType = JOIN_STYPE_NONE;
×
2012
        } else {
2013
          pJoin->joinType = JOIN_TYPE_RIGHT;
2✔
2014
        }
2015
      } else if (tableResNonNull[0] && !tableResOp[1]) {
341!
2016
        pJoin->joinType = JOIN_TYPE_LEFT;
4✔
2017
      }
2018
      break;
343✔
2019
    default:
×
2020
      break;
×
2021
  }
2022

2023
  return TSDB_CODE_SUCCESS;
1,444✔
2024
}
2025

2026

2027
static int32_t pdcDealJoin(SOptimizeContext* pCxt, SJoinLogicNode* pJoin) {
442,501✔
2028
  if (OPTIMIZE_FLAG_TEST_MASK(pJoin->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE)) {
442,501✔
2029
    return TSDB_CODE_SUCCESS;
263,168✔
2030
  }
2031
  if (pJoin->joinAlgo != JOIN_ALGO_UNKNOWN) {
179,333✔
2032
    return TSDB_CODE_SUCCESS;
52,478✔
2033
  }
2034

2035
  EJoinType    t = pJoin->joinType;
126,855✔
2036
  EJoinSubType s = pJoin->subType;
126,855✔
2037
  SNode*       pOnCond = NULL;
126,855✔
2038
  SNode*       pLeftChildCond = NULL;
126,855✔
2039
  SNode*       pRightChildCond = NULL;
126,855✔
2040
  int32_t      code = pdcJoinCheckAllCond(pCxt, pJoin);
126,855✔
2041
  while (true) {
2042
    if (TSDB_CODE_SUCCESS == code && NULL != pJoin->node.pConditions) {
127,220✔
2043
      if (0 != gJoinWhereOpt[t][s].pushDownFlag) {
108,717✔
2044
        code = pdcJoinSplitCond(pJoin, &pJoin->node.pConditions, &pOnCond, &pLeftChildCond, &pRightChildCond, true);
108,656✔
2045
        if (TSDB_CODE_SUCCESS == code && NULL != pOnCond) {
108,656!
2046
          code = pdcJoinPushDownOnCond(pCxt, pJoin, &pOnCond);
56,556✔
2047
        }
2048
        if (TSDB_CODE_SUCCESS == code && NULL != pLeftChildCond) {
108,656!
2049
          code = pdcPushDownCondToChild(pCxt, (SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 0), &pLeftChildCond);
67,624✔
2050
        }
2051
        if (TSDB_CODE_SUCCESS == code && NULL != pRightChildCond) {
108,656!
2052
          code =
2053
              pdcPushDownCondToChild(pCxt, (SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 1), &pRightChildCond);
60,646✔
2054
        }
2055
      }
2056
      if (TSDB_CODE_SUCCESS == code && NULL != pJoin->node.pConditions) {
108,717!
2057
        code = pdcRewriteTypeBasedOnConds(pCxt, pJoin);
1,092✔
2058
      }
2059
    }
2060

2061
    if (TSDB_CODE_SUCCESS == code) {
127,220✔
2062
      code = pdcRewriteTypeBasedOnJoinRes(pCxt, pJoin);
126,586✔
2063
    }
2064

2065
    if (TSDB_CODE_SUCCESS != code || t == pJoin->joinType) {
127,220✔
2066
      break;
2067
    }
2068

2069
    t = pJoin->joinType;
365✔
2070
    s = pJoin->subType;
365✔
2071
  }
2072

2073
  if (TSDB_CODE_SUCCESS == code && NULL != pJoin->pFullOnCond && 0 != gJoinOnOpt[t][s].pushDownFlag) {
126,855✔
2074
    code = pdcJoinSplitCond(pJoin, &pJoin->pFullOnCond, NULL, &pLeftChildCond, &pRightChildCond, false);
123,570✔
2075
    if (TSDB_CODE_SUCCESS == code && NULL != pLeftChildCond) {
123,570!
2076
      code = pdcPushDownCondToChild(pCxt, (SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 0), &pLeftChildCond);
547✔
2077
    }
2078
    if (TSDB_CODE_SUCCESS == code && NULL != pRightChildCond) {
123,570!
2079
      code = pdcPushDownCondToChild(pCxt, (SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 1), &pRightChildCond);
477✔
2080
    }
2081
  }
2082

2083
  if (TSDB_CODE_SUCCESS == code && NULL != pJoin->pFullOnCond && !IS_WINDOW_JOIN(pJoin->subType) &&
126,855✔
2084
      NULL == pJoin->addPrimEqCond && NULL == pJoin->pPrimKeyEqCond) {
124,482✔
2085
    code = pdcJoinSplitPrimEqCond(pCxt, pJoin);
124,066✔
2086
  }
2087

2088
  if (TSDB_CODE_SUCCESS == code) {
126,855✔
2089
    code = pdcJoinPartEqualOnCond(pCxt, pJoin);
126,221✔
2090
  }
2091

2092
  if (TSDB_CODE_SUCCESS == code) {
126,855✔
2093
    code = pdcJoinHandleGrpJoinCond(pCxt, pJoin);
126,221✔
2094
  }
2095

2096
  if (TSDB_CODE_SUCCESS == code) {
126,855✔
2097
    code = pdcJoinAddParentOnColsToTarget(pCxt, pJoin);
126,184✔
2098
  }
2099

2100
  // if (TSDB_CODE_SUCCESS == code) {
2101
  //   code = pdcJoinAddPreFilterColsToTarget(pCxt, pJoin);
2102
  // }
2103

2104
  if (TSDB_CODE_SUCCESS == code) {
126,855✔
2105
    code = pdcJoinAddFilterColsToTarget(pCxt, pJoin);
126,184✔
2106
  }
2107

2108
  if (TSDB_CODE_SUCCESS == code) {
126,855✔
2109
    OPTIMIZE_FLAG_SET_MASK(pJoin->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE);
126,184✔
2110
    pCxt->optimized = true;
126,184✔
2111
  } else {
2112
    nodesDestroyNode(pOnCond);
671✔
2113
    nodesDestroyNode(pLeftChildCond);
671✔
2114
    nodesDestroyNode(pRightChildCond);
671✔
2115
  }
2116

2117
  return code;
126,855✔
2118
}
2119

2120
typedef struct SPartAggCondContext {
2121
  SAggLogicNode* pAgg;
2122
  bool           hasAggFunc;
2123
} SPartAggCondContext;
2124

2125
static EDealRes partAggCondHasAggFuncImpl(SNode* pNode, void* pContext) {
230,204✔
2126
  SPartAggCondContext* pCxt = pContext;
230,204✔
2127
  if (QUERY_NODE_COLUMN == nodeType(pNode)) {
230,204✔
2128
    SNode* pAggFunc = NULL;
115,012✔
2129
    FOREACH(pAggFunc, pCxt->pAgg->pAggFuncs) {
115,667✔
2130
      if (strcmp(((SColumnNode*)pNode)->colName, ((SFunctionNode*)pAggFunc)->node.aliasName) == 0) {
47,268✔
2131
        pCxt->hasAggFunc = true;
46,613✔
2132
        return DEAL_RES_END;
46,613✔
2133
      }
2134
    }
2135
  }
2136
  return DEAL_RES_CONTINUE;
183,591✔
2137
}
2138

2139
static int32_t partitionAggCondHasAggFunc(SAggLogicNode* pAgg, SNode* pCond) {
115,011✔
2140
  SPartAggCondContext cxt = {.pAgg = pAgg, .hasAggFunc = false};
115,011✔
2141
  nodesWalkExpr(pCond, partAggCondHasAggFuncImpl, &cxt);
115,011✔
2142
  return cxt.hasAggFunc;
115,011✔
2143
}
2144

2145
static int32_t partitionAggCondConj(SAggLogicNode* pAgg, SNode** ppAggFuncCond, SNode** ppGroupKeyCond) {
43✔
2146
  SLogicConditionNode* pLogicCond = (SLogicConditionNode*)pAgg->node.pConditions;
43✔
2147
  int32_t              code = TSDB_CODE_SUCCESS;
43✔
2148

2149
  SNodeList* pAggFuncConds = NULL;
43✔
2150
  SNodeList* pGroupKeyConds = NULL;
43✔
2151
  SNode*     pCond = NULL;
43✔
2152
  FOREACH(pCond, pLogicCond->pParameterList) {
129!
2153
    SNode* pNew = NULL;
86✔
2154
    code = nodesCloneNode(pCond, &pNew);
86✔
2155
    if (TSDB_CODE_SUCCESS != code) {
86!
2156
      break;
×
2157
    }
2158
    if (partitionAggCondHasAggFunc(pAgg, pCond)) {
86✔
2159
      code = nodesListMakeStrictAppend(&pAggFuncConds, pNew);
84✔
2160
    } else {
2161
      code = nodesListMakeStrictAppend(&pGroupKeyConds, pNew);
2✔
2162
    }
2163
    if (TSDB_CODE_SUCCESS != code) {
86!
2164
      break;
×
2165
    }
2166
  }
2167

2168
  SNode* pTempAggFuncCond = NULL;
43✔
2169
  SNode* pTempGroupKeyCond = NULL;
43✔
2170
  if (TSDB_CODE_SUCCESS == code) {
43!
2171
    code = nodesMergeConds(&pTempAggFuncCond, &pAggFuncConds);
43✔
2172
  }
2173
  if (TSDB_CODE_SUCCESS == code) {
43!
2174
    code = nodesMergeConds(&pTempGroupKeyCond, &pGroupKeyConds);
43✔
2175
  }
2176

2177
  if (TSDB_CODE_SUCCESS == code) {
43!
2178
    *ppAggFuncCond = pTempAggFuncCond;
43✔
2179
    *ppGroupKeyCond = pTempGroupKeyCond;
43✔
2180
  } else {
2181
    nodesDestroyList(pAggFuncConds);
×
2182
    nodesDestroyList(pGroupKeyConds);
×
2183
    nodesDestroyNode(pTempAggFuncCond);
×
2184
    nodesDestroyNode(pTempGroupKeyCond);
×
2185
  }
2186
  nodesDestroyNode(pAgg->node.pConditions);
43✔
2187
  pAgg->node.pConditions = NULL;
43✔
2188
  return code;
43✔
2189
}
2190

2191
static int32_t partitionAggCond(SAggLogicNode* pAgg, SNode** ppAggFunCond, SNode** ppGroupKeyCond) {
114,968✔
2192
  SNode* pAggNodeCond = pAgg->node.pConditions;
114,968✔
2193
  if (QUERY_NODE_LOGIC_CONDITION == nodeType(pAggNodeCond) &&
114,968✔
2194
      LOGIC_COND_TYPE_AND == ((SLogicConditionNode*)(pAggNodeCond))->condType) {
49✔
2195
    return partitionAggCondConj(pAgg, ppAggFunCond, ppGroupKeyCond);
43✔
2196
  }
2197
  if (partitionAggCondHasAggFunc(pAgg, pAggNodeCond)) {
114,925✔
2198
    *ppAggFunCond = pAggNodeCond;
46,529✔
2199
  } else {
2200
    *ppGroupKeyCond = pAggNodeCond;
68,396✔
2201
  }
2202
  pAgg->node.pConditions = NULL;
114,925✔
2203
  return TSDB_CODE_SUCCESS;
114,925✔
2204
}
2205

2206
static int32_t pushCondToAggCond(SOptimizeContext* pCxt, SAggLogicNode* pAgg, SNode** pAggFuncCond) {
46,572✔
2207
  return nodesMergeNode(&pAgg->node.pConditions, pAggFuncCond);
46,572✔
2208
}
2209

2210
typedef struct SRewriteAggGroupKeyCondContext {
2211
  SAggLogicNode* pAgg;
2212
  int32_t        errCode;
2213
} SRewriteAggGroupKeyCondContext;
2214

2215
static EDealRes rewriteAggGroupKeyCondForPushDownImpl(SNode** pNode, void* pContext) {
136,954✔
2216
  SRewriteAggGroupKeyCondContext* pCxt = pContext;
136,954✔
2217
  SAggLogicNode*                  pAgg = pCxt->pAgg;
136,954✔
2218
  if (QUERY_NODE_COLUMN == nodeType(*pNode)) {
136,954✔
2219
    SNode* pGroupKey = NULL;
68,399✔
2220
    FOREACH(pGroupKey, pAgg->pGroupKeys) {
68,400!
2221
      SNode* pGroup = NULL;
68,400✔
2222
      FOREACH(pGroup, ((SGroupingSetNode*)pGroupKey)->pParameterList) {
68,401!
2223
        if (0 == strcmp(((SExprNode*)pGroup)->aliasName, ((SColumnNode*)(*pNode))->colName)) {
68,400✔
2224
          SNode* pExpr = NULL;
68,399✔
2225
          pCxt->errCode = nodesCloneNode(pGroup, &pExpr);
68,399✔
2226
          if (pExpr == NULL) {
68,399!
2227
            return DEAL_RES_ERROR;
×
2228
          }
2229
          nodesDestroyNode(*pNode);
68,399✔
2230
          *pNode = pExpr;
68,399✔
2231
          return DEAL_RES_IGNORE_CHILD;
68,399✔
2232
        }
2233
      }
2234
    }
2235
  }
2236
  return DEAL_RES_CONTINUE;
68,555✔
2237
}
2238

2239
static int32_t rewriteAggGroupKeyCondForPushDown(SOptimizeContext* pCxt, SAggLogicNode* pAgg, SNode* pGroupKeyCond) {
68,398✔
2240
  SRewriteAggGroupKeyCondContext cxt = {.pAgg = pAgg, .errCode = TSDB_CODE_SUCCESS};
68,398✔
2241
  nodesRewriteExpr(&pGroupKeyCond, rewriteAggGroupKeyCondForPushDownImpl, &cxt);
68,398✔
2242
  return cxt.errCode;
68,398✔
2243
}
2244

2245
static int32_t pdcDealAgg(SOptimizeContext* pCxt, SAggLogicNode* pAgg) {
1,897,895✔
2246
  if (NULL == pAgg->node.pConditions ||
1,897,895✔
2247
      OPTIMIZE_FLAG_TEST_MASK(pAgg->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE)) {
259,807✔
2248
    return TSDB_CODE_SUCCESS;
1,782,927✔
2249
  }
2250
  // TODO: remove it after full implementation of pushing down to child
2251
  if (1 != LIST_LENGTH(pAgg->node.pChildren)) {
114,968!
2252
    return TSDB_CODE_SUCCESS;
×
2253
  }
2254

2255
  SNode*  pAggFuncCond = NULL;
114,968✔
2256
  SNode*  pGroupKeyCond = NULL;
114,968✔
2257
  int32_t code = partitionAggCond(pAgg, &pAggFuncCond, &pGroupKeyCond);
114,968✔
2258
  if (TSDB_CODE_SUCCESS == code && NULL != pAggFuncCond) {
114,968!
2259
    code = pushCondToAggCond(pCxt, pAgg, &pAggFuncCond);
46,572✔
2260
  }
2261
  if (TSDB_CODE_SUCCESS == code && NULL != pGroupKeyCond) {
114,968!
2262
    code = rewriteAggGroupKeyCondForPushDown(pCxt, pAgg, pGroupKeyCond);
68,398✔
2263
  }
2264
  if (TSDB_CODE_SUCCESS == code && NULL != pGroupKeyCond) {
114,968!
2265
    SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pAgg->node.pChildren, 0);
68,398✔
2266
    code = pdcPushDownCondToChild(pCxt, pChild, &pGroupKeyCond);
68,398✔
2267
  }
2268
  if (TSDB_CODE_SUCCESS == code) {
114,968!
2269
    OPTIMIZE_FLAG_SET_MASK(pAgg->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE);
114,968✔
2270
    pCxt->optimized = true;
114,968✔
2271
  } else {
2272
    nodesDestroyNode(pGroupKeyCond);
×
2273
    nodesDestroyNode(pAggFuncCond);
×
2274
  }
2275
  return code;
114,968✔
2276
}
2277

2278
typedef struct SRewriteProjCondContext {
2279
  SProjectLogicNode* pProj;
2280
  int32_t            errCode;
2281
} SRewriteProjCondContext;
2282

2283
static EDealRes rewriteProjectCondForPushDownImpl(SNode** ppNode, void* pContext) {
65,469✔
2284
  SRewriteProjCondContext* pCxt = pContext;
65,469✔
2285
  SProjectLogicNode*       pProj = pCxt->pProj;
65,469✔
2286
  if (QUERY_NODE_COLUMN == nodeType(*ppNode)) {
65,469✔
2287
    SNode* pTarget = NULL;
19,530✔
2288
    FOREACH(pTarget, pProj->node.pTargets) {
44,625!
2289
      if (nodesEqualNode(pTarget, *ppNode)) {
44,625✔
2290
        SNode* pProjection = NULL;
19,530✔
2291
        FOREACH(pProjection, pProj->pProjections) {
44,625!
2292
          if (0 == strcmp(((SExprNode*)pProjection)->aliasName, ((SColumnNode*)(*ppNode))->colName)) {
44,625✔
2293
            SNode* pExpr = NULL;
19,530✔
2294
            pCxt->errCode = nodesCloneNode(pProjection, &pExpr);
19,530✔
2295
            if (pExpr == NULL) {
19,530!
2296
              return DEAL_RES_ERROR;
×
2297
            }
2298
            nodesDestroyNode(*ppNode);
19,530✔
2299
            *ppNode = pExpr;
19,530✔
2300
            return DEAL_RES_IGNORE_CHILD;
19,530✔
2301
          }  // end if expr alias name equal column name
2302
        }    // end for each project
2303
      }      // end if target node equals cond column node
2304
    }        // end for each targets
2305
  }
2306
  return DEAL_RES_CONTINUE;
45,939✔
2307
}
2308

2309
static int32_t rewriteProjectCondForPushDown(SOptimizeContext* pCxt, SProjectLogicNode* pProject,
12,489✔
2310
                                             SNode** ppProjectCond) {
2311
  SRewriteProjCondContext cxt = {.pProj = pProject, .errCode = TSDB_CODE_SUCCESS};
12,489✔
2312
  SNode*                  pProjectCond = pProject->node.pConditions;
12,489✔
2313
  nodesRewriteExpr(&pProjectCond, rewriteProjectCondForPushDownImpl, &cxt);
12,489✔
2314
  *ppProjectCond = pProjectCond;
12,489✔
2315
  pProject->node.pConditions = NULL;
12,489✔
2316
  return cxt.errCode;
12,489✔
2317
}
2318

2319
static int32_t pdcDealProject(SOptimizeContext* pCxt, SProjectLogicNode* pProject) {
3,703,240✔
2320
  if (NULL == pProject->node.pConditions ||
3,703,240✔
2321
      OPTIMIZE_FLAG_TEST_MASK(pProject->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE)) {
12,797!
2322
    return TSDB_CODE_SUCCESS;
3,690,443✔
2323
  }
2324
  // TODO: remove it after full implementation of pushing down to child
2325
  if (1 != LIST_LENGTH(pProject->node.pChildren)) {
12,797!
2326
    return TSDB_CODE_SUCCESS;
276✔
2327
  }
2328

2329
  if (NULL != pProject->node.pLimit || NULL != pProject->node.pSlimit) {
12,521!
2330
    return TSDB_CODE_SUCCESS;
32✔
2331
  }
2332
  SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pProject->node.pChildren, 0);
12,489✔
2333
  if(pChild->pLimit != NULL) {
12,489!
2334
    return TSDB_CODE_SUCCESS;
×
2335
  }
2336

2337
  int32_t code = TSDB_CODE_SUCCESS;
12,489✔
2338
  SNode*  pProjCond = NULL;
12,489✔
2339
  code = rewriteProjectCondForPushDown(pCxt, pProject, &pProjCond);
12,489✔
2340
  if (TSDB_CODE_SUCCESS == code) {
12,489!
2341
    code = pdcPushDownCondToChild(pCxt, pChild, &pProjCond);
12,489✔
2342
  }
2343

2344
  if (TSDB_CODE_SUCCESS == code) {
12,489!
2345
    OPTIMIZE_FLAG_SET_MASK(pProject->node.optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE);
12,489✔
2346
    pCxt->optimized = true;
12,489✔
2347
  } else {
2348
    nodesDestroyNode(pProjCond);
×
2349
  }
2350
  return code;
12,489✔
2351
}
2352

2353
static int32_t pdcTrivialPushDown(SOptimizeContext* pCxt, SLogicNode* pLogicNode) {
1,286,712✔
2354
  if (NULL == pLogicNode->pConditions ||
1,286,712✔
2355
      OPTIMIZE_FLAG_TEST_MASK(pLogicNode->optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE)) {
1,801!
2356
    return TSDB_CODE_SUCCESS;
1,284,911✔
2357
  }
2358
  SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pLogicNode->pChildren, 0);
1,801✔
2359
  int32_t     code = pdcPushDownCondToChild(pCxt, pChild, &pLogicNode->pConditions);
1,801✔
2360
  if (TSDB_CODE_SUCCESS == code) {
1,801!
2361
    OPTIMIZE_FLAG_SET_MASK(pLogicNode->optimizedFlag, OPTIMIZE_FLAG_PUSH_DOWN_CONDE);
1,801✔
2362
    pCxt->optimized = true;
1,801✔
2363
  }
2364
  return code;
1,801✔
2365
}
2366

2367
static int32_t pdcDealVirtualTable(SOptimizeContext* pCxt, SVirtualScanLogicNode* pVScan) {
19,974✔
2368
  // TODO: remove it after full implementation of pushing down to child
2369
  if (1 != LIST_LENGTH(pVScan->node.pChildren) || 0 != LIST_LENGTH(pVScan->pScanPseudoCols) || pVScan->tableType == TSDB_SUPER_TABLE) {
19,974!
2370
    return TSDB_CODE_SUCCESS;
15,430✔
2371
  }
2372
  return pdcTrivialPushDown(pCxt, (SLogicNode*)pVScan);
4,544✔
2373
}
2374

2375
static int32_t pdcOptimizeImpl(SOptimizeContext* pCxt, SLogicNode* pLogicNode) {
11,200,934✔
2376
  int32_t code = TSDB_CODE_SUCCESS;
11,200,934✔
2377
  switch (nodeType(pLogicNode)) {
11,200,934✔
2378
    case QUERY_NODE_LOGIC_PLAN_SCAN:
3,356,282✔
2379
      code = pdcDealScan(pCxt, (SScanLogicNode*)pLogicNode);
3,356,282✔
2380
      break;
3,356,254✔
2381
    case QUERY_NODE_LOGIC_PLAN_JOIN:
442,501✔
2382
      code = pdcDealJoin(pCxt, (SJoinLogicNode*)pLogicNode);
442,501✔
2383
      break;
442,501✔
2384
    case QUERY_NODE_LOGIC_PLAN_AGG:
1,897,967✔
2385
      code = pdcDealAgg(pCxt, (SAggLogicNode*)pLogicNode);
1,897,967✔
2386
      break;
1,897,909✔
2387
    case QUERY_NODE_LOGIC_PLAN_PROJECT:
3,703,323✔
2388
      code = pdcDealProject(pCxt, (SProjectLogicNode*)pLogicNode);
3,703,323✔
2389
      break;
3,703,279✔
2390
    case QUERY_NODE_LOGIC_PLAN_SORT:
1,282,168✔
2391
    case QUERY_NODE_LOGIC_PLAN_PARTITION:
2392
      code = pdcTrivialPushDown(pCxt, pLogicNode);
1,282,168✔
2393
      break;
1,282,167✔
2394
    case QUERY_NODE_LOGIC_PLAN_VIRTUAL_TABLE_SCAN:
19,974✔
2395
      code = pdcDealVirtualTable(pCxt, (SVirtualScanLogicNode*)pLogicNode);
19,974✔
2396
      break;
19,974✔
2397
    default:
498,719✔
2398
      break;
498,719✔
2399
  }
2400
  if (TSDB_CODE_SUCCESS == code) {
11,200,803✔
2401
    SNode* pChild = NULL;
11,200,079✔
2402
    FOREACH(pChild, pLogicNode->pChildren) {
19,884,987✔
2403
      code = pdcOptimizeImpl(pCxt, (SLogicNode*)pChild);
8,685,621✔
2404
      if (TSDB_CODE_SUCCESS != code) {
8,685,654✔
2405
        break;
746✔
2406
      }
2407
    }
2408
  }
2409
  return code;
11,200,836✔
2410
}
2411

2412
static int32_t pdcOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
2,515,253✔
2413
  return pdcOptimizeImpl(pCxt, pLogicSubplan->pNode);
2,515,253✔
2414
}
2415

2416
static bool eliminateNotNullCondMayBeOptimized(SLogicNode* pNode, void* pCtx) {
8,730,576✔
2417
  if (QUERY_NODE_LOGIC_PLAN_AGG != nodeType(pNode)) {
8,730,576✔
2418
    return false;
7,246,991✔
2419
  }
2420

2421
  SAggLogicNode* pAgg = (SAggLogicNode*)pNode;
1,483,585✔
2422
  if (pNode->pChildren->length != 1 || NULL != pAgg->pGroupKeys) {
1,483,585✔
2423
    return false;
439,864✔
2424
  }
2425

2426
  SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pAgg->node.pChildren, 0);
1,043,721✔
2427
  if (QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(pChild)) {
1,043,711✔
2428
    return false;
355,644✔
2429
  }
2430

2431
  SScanLogicNode* pScan = (SScanLogicNode*)pChild;
688,067✔
2432
  if (NULL == pScan->node.pConditions || QUERY_NODE_OPERATOR != nodeType(pScan->node.pConditions)) {
688,067✔
2433
    return false;
663,998✔
2434
  }
2435

2436
  SOperatorNode* pOp = (SOperatorNode*)pScan->node.pConditions;
24,069✔
2437
  if (OP_TYPE_IS_NOT_NULL != pOp->opType) {
24,069✔
2438
    return false;
21,562✔
2439
  }
2440

2441
  if (QUERY_NODE_COLUMN != nodeType(pOp->pLeft)) {
2,507!
2442
    return false;
×
2443
  }
2444

2445
  SNode* pTmp = NULL;
2,507✔
2446
  FOREACH(pTmp, pAgg->pAggFuncs) {
2,507!
2447
    SFunctionNode* pFunc = (SFunctionNode*)pTmp;
2,507✔
2448
    if (!fmIsIgnoreNullFunc(pFunc->funcId)) {
2,507✔
2449
      return false;
2,085✔
2450
    }
2451
    if (fmIsMultiResFunc(pFunc->funcId)) {
422✔
2452
      SNode* pParam = NULL;
334✔
2453
      FOREACH(pParam, pFunc->pParameterList) {
334!
2454
        if (QUERY_NODE_COLUMN != nodeType(pParam)) {
334!
2455
          return false;
×
2456
        }
2457
        if (!nodesEqualNode(pParam, pOp->pLeft)) {
334!
2458
          return false;
334✔
2459
        }
2460
      }
2461
    } else {
2462
      SNode* pParam = nodesListGetNode(pFunc->pParameterList, 0);
88✔
2463
      if (QUERY_NODE_COLUMN != nodeType(pParam)) {
88!
2464
        return false;
×
2465
      }
2466
      if (!nodesEqualNode(pParam, pOp->pLeft)) {
88!
2467
        return false;
88✔
2468
      }
2469
    }
2470
  }
2471

2472
  return true;
×
2473
}
2474

2475
static int32_t eliminateNotNullCondOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
2,017,956✔
2476
  SLogicNode* pNode = (SLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, eliminateNotNullCondMayBeOptimized, NULL);
2,017,956✔
2477
  if (NULL == pNode) {
2,018,053!
2478
    return TSDB_CODE_SUCCESS;
2,018,059✔
2479
  }
2480

2481
  SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(pNode->pChildren, 0);
×
2482
  nodesDestroyNode(pScan->node.pConditions);
×
2483
  pScan->node.pConditions = NULL;
×
2484

2485
  pCxt->optimized = true;
×
2486

2487
  return TSDB_CODE_SUCCESS;
×
2488
}
2489

2490
static bool sortPriKeyOptIsPriKeyOrderBy(SNodeList* pSortKeys) {
832,116✔
2491
  if (1 != LIST_LENGTH(pSortKeys)) {
832,116!
2492
    return false;
148,943✔
2493
  }
2494
  SNode* pNode = ((SOrderByExprNode*)nodesListGetNode(pSortKeys, 0))->pExpr;
683,173✔
2495
  return (QUERY_NODE_COLUMN == nodeType(pNode) ? isPrimaryKeyImpl(pNode) : false);
683,173✔
2496
}
2497

2498
static bool sortPriKeyOptMayBeOptimized(SLogicNode* pNode, void* pCtx) {
8,492,709✔
2499
  if (QUERY_NODE_LOGIC_PLAN_SORT != nodeType(pNode)) {
8,492,709✔
2500
    return false;
7,680,549✔
2501
  }
2502
  SSortLogicNode* pSort = (SSortLogicNode*)pNode;
812,160✔
2503
  if (pSort->skipPKSortOpt || !sortPriKeyOptIsPriKeyOrderBy(pSort->pSortKeys) ||
812,160✔
2504
      1 != LIST_LENGTH(pSort->node.pChildren)) {
153,127!
2505
    return false;
659,034✔
2506
  }
2507
  SNode* pChild = nodesListGetNode(pSort->node.pChildren, 0);
153,127✔
2508
  if (QUERY_NODE_LOGIC_PLAN_JOIN == nodeType(pChild)) {
153,126✔
2509
    SJoinLogicNode* pJoin = (SJoinLogicNode*)pChild;
17,208✔
2510
    if (JOIN_TYPE_FULL == pJoin->joinType) {
17,208✔
2511
      return false;
23✔
2512
    }
2513
  }
2514

2515
  FOREACH(pChild, pSort->node.pChildren) {
306,104!
2516
    SLogicNode* pSortDescendent = optFindPossibleNode((SLogicNode*)pChild, sortPriKeyOptMayBeOptimized, NULL);
153,104✔
2517
    if (pSortDescendent != NULL) {
153,104✔
2518
      return false;
103✔
2519
    }
2520
  }
2521
  return true;
153,000✔
2522
}
2523

2524
static int32_t sortPriKeyOptHandleLeftRightJoinSort(SJoinLogicNode* pJoin, SSortLogicNode* pSort, bool* pNotOptimize,
563✔
2525
                                                    bool* keepSort) {
2526
  if (JOIN_STYPE_SEMI == pJoin->subType || JOIN_STYPE_NONE == pJoin->subType) {
563!
2527
    return TSDB_CODE_SUCCESS;
126✔
2528
  }
2529

2530
  SSHashObj* pLeftTables = NULL;
437✔
2531
  SSHashObj* pRightTables = NULL;
437✔
2532
  bool       sortByProbe = true;
437✔
2533
  /*
2534
    bool sortByLeft = true, sortByRight = true, sortByProbe = false;
2535
    collectTableAliasFromNodes(nodesListGetNode(pJoin->node.pChildren, 0), &pLeftTables);
2536
    collectTableAliasFromNodes(nodesListGetNode(pJoin->node.pChildren, 1), &pRightTables);
2537

2538
    SOrderByExprNode* pExprNode = (SOrderByExprNode*)nodesListGetNode(pSort->pSortKeys, 0);
2539
    SColumnNode* pSortCol = (SColumnNode*)pExprNode->pExpr;
2540
    if (NULL == tSimpleHashGet(pLeftTables, pSortCol->tableAlias, strlen(pSortCol->tableAlias))) {
2541
      sortByLeft = false;
2542
    }
2543
    if (NULL == tSimpleHashGet(pRightTables, pSortCol->tableAlias, strlen(pSortCol->tableAlias))) {
2544
      sortByRight = false;
2545
    }
2546

2547
    tSimpleHashCleanup(pLeftTables);
2548
    tSimpleHashCleanup(pRightTables);
2549

2550
    if (!sortByLeft && !sortByRight) {
2551
      planError("sort by primary key not in any join subtable, tableAlias: %s", pSortCol->tableAlias);
2552
      return TSDB_CODE_PLAN_INTERNAL_ERROR;
2553
    }
2554

2555
    if (sortByLeft && sortByRight) {
2556
      planError("sort by primary key in both join subtables, tableAlias: %s", pSortCol->tableAlias);
2557
      return TSDB_CODE_PLAN_INTERNAL_ERROR;
2558
    }
2559

2560
    if ((JOIN_TYPE_LEFT == pJoin->joinType && sortByLeft) || (JOIN_TYPE_RIGHT == pJoin->joinType && sortByRight)) {
2561
      sortByProbe = true;
2562
    }
2563
  */
2564
  switch (pJoin->subType) {
437!
2565
    case JOIN_STYPE_OUTER: {
204✔
2566
      if (sortByProbe) {
204!
2567
        return TSDB_CODE_SUCCESS;
204✔
2568
      }
2569
    }
2570
    case JOIN_STYPE_ANTI: {
2571
      if (sortByProbe) {
72!
2572
        return TSDB_CODE_SUCCESS;
72✔
2573
      }
2574
    }
2575
    case JOIN_STYPE_ASOF:
2576
    case JOIN_STYPE_WIN: {
2577
      if (sortByProbe) {
161!
2578
        if (NULL != pJoin->pLeftEqNodes && pJoin->pLeftEqNodes->length > 0) {
161!
2579
          *pNotOptimize = true;
14✔
2580
        }
2581
        return TSDB_CODE_SUCCESS;
161✔
2582
      }
2583
    }
2584
    default:
2585
      return TSDB_CODE_PLAN_INTERNAL_ERROR;
×
2586
  }
2587

2588
  return TSDB_CODE_SUCCESS;
2589
}
2590

2591
static int32_t sortPriKeyOptHandleJoinSort(SLogicNode* pNode, bool groupSort, SSortLogicNode* pSort, bool* pNotOptimize,
22,072✔
2592
                                           SNodeList** pSequencingNodes, bool* keepSort) {
2593
  SJoinLogicNode* pJoin = (SJoinLogicNode*)pNode;
22,072✔
2594
  int32_t         code = TSDB_CODE_SUCCESS;
22,072✔
2595

2596
  switch (pJoin->joinType) {
22,072✔
2597
    case JOIN_TYPE_LEFT:
563✔
2598
    case JOIN_TYPE_RIGHT: {
2599
      code = sortPriKeyOptHandleLeftRightJoinSort(pJoin, pSort, pNotOptimize, keepSort);
563✔
2600
      if (TSDB_CODE_SUCCESS != code) {
563!
2601
        return code;
×
2602
      }
2603
      if (*pNotOptimize || !(*keepSort)) {
563!
2604
        return TSDB_CODE_SUCCESS;
14✔
2605
      }
2606
      break;
549✔
2607
    }
2608
    default:
21,509✔
2609
      break;
21,509✔
2610
  }
2611

2612
  code = sortPriKeyOptGetSequencingNodesImpl((SLogicNode*)nodesListGetNode(pNode->pChildren, 0), groupSort, pSort,
22,058✔
2613
                                             pNotOptimize, pSequencingNodes, keepSort);
2614
  if (TSDB_CODE_SUCCESS == code) {
22,058!
2615
    code = sortPriKeyOptGetSequencingNodesImpl((SLogicNode*)nodesListGetNode(pNode->pChildren, 1), groupSort, pSort,
22,058✔
2616
                                               pNotOptimize, pSequencingNodes, keepSort);
2617
  }
2618

2619
  return code;
22,058✔
2620
}
2621

2622
static EOrder sortPriKeyOptGetPriKeyOrder(SSortLogicNode* pSort) {
367,711✔
2623
  return ((SOrderByExprNode*)nodesListGetNode(pSort->pSortKeys, 0))->order;
367,711✔
2624
}
2625

2626
static bool sortPriKeyOptHasUnsupportedPkFunc(SLogicNode* pLogicNode, EOrder sortOrder) {
214,816✔
2627
  if (sortOrder == ORDER_ASC) {
214,816✔
2628
    return false;
184,279✔
2629
  }
2630

2631
  SNodeList* pFuncList = NULL;
30,537✔
2632
  switch (nodeType(pLogicNode)) {
30,537!
2633
    case QUERY_NODE_LOGIC_PLAN_AGG:
446✔
2634
      pFuncList = ((SAggLogicNode*)pLogicNode)->pAggFuncs;
446✔
2635
      break;
446✔
2636
    case QUERY_NODE_LOGIC_PLAN_WINDOW:
2,580✔
2637
      pFuncList = ((SWindowLogicNode*)pLogicNode)->pFuncs;
2,580✔
2638
      break;
2,580✔
2639
    case QUERY_NODE_LOGIC_PLAN_PARTITION:
2,440✔
2640
      pFuncList = ((SPartitionLogicNode*)pLogicNode)->pAggFuncs;
2,440✔
2641
      break;
2,440✔
2642
    case QUERY_NODE_LOGIC_PLAN_INDEF_ROWS_FUNC:
×
2643
      pFuncList = ((SIndefRowsFuncLogicNode*)pLogicNode)->pFuncs;
×
2644
      break;
×
2645
    case QUERY_NODE_LOGIC_PLAN_INTERP_FUNC:
×
2646
      pFuncList = ((SInterpFuncLogicNode*)pLogicNode)->pFuncs;
×
2647
      break;
×
2648
    case QUERY_NODE_LOGIC_PLAN_FORECAST_FUNC:
×
2649
      pFuncList = ((SForecastFuncLogicNode*)pLogicNode)->pFuncs;
×
2650
    default:
25,071✔
2651
      break;
25,071✔
2652
  }
2653

2654
  SNode* pNode = 0;
30,537✔
2655
  FOREACH(pNode, pFuncList) {
40,408✔
2656
    if (nodeType(pNode) != QUERY_NODE_FUNCTION) {
9,871!
2657
      continue;
×
2658
    }
2659
    SFunctionNode* pFuncNode = (SFunctionNode*)pLogicNode;
9,871✔
2660
    if (pFuncNode->hasPk &&
9,871!
2661
        (pFuncNode->funcType == FUNCTION_TYPE_DIFF || pFuncNode->funcType == FUNCTION_TYPE_DERIVATIVE ||
×
2662
         pFuncNode->funcType == FUNCTION_TYPE_IRATE || pFuncNode->funcType == FUNCTION_TYPE_TWA)) {
×
2663
      return true;
×
2664
    }
2665
  }
2666
  return false;
30,537✔
2667
}
2668

2669
int32_t sortPriKeyOptGetSequencingNodesImpl(SLogicNode* pNode, bool groupSort, SSortLogicNode* pSort,
214,815✔
2670
                                            bool* pNotOptimize, SNodeList** pSequencingNodes, bool* keepSort) {
2671
  EOrder sortOrder = sortPriKeyOptGetPriKeyOrder(pSort);
214,815✔
2672
  if (sortPriKeyOptHasUnsupportedPkFunc(pNode, sortOrder)) {
214,816!
2673
    *pNotOptimize = true;
×
2674
    return TSDB_CODE_SUCCESS;
×
2675
  }
2676

2677
  if (NULL != pNode->pLimit || NULL != pNode->pSlimit) {
214,816✔
2678
    *pNotOptimize = false;
4,924✔
2679
    return TSDB_CODE_SUCCESS;
4,924✔
2680
  }
2681

2682
  switch (nodeType(pNode)) {
209,892✔
2683
    case QUERY_NODE_LOGIC_PLAN_SCAN: {
138,882✔
2684
      SScanLogicNode* pScan = (SScanLogicNode*)pNode;
138,882✔
2685
      if ((!groupSort && NULL != pScan->pGroupTags) || TSDB_SYSTEM_TABLE == pScan->tableType) {
138,882!
2686
        *pNotOptimize = true;
16✔
2687
        return TSDB_CODE_SUCCESS;
16✔
2688
      }
2689
      return nodesListMakeAppend(pSequencingNodes, (SNode*)pNode);
138,866✔
2690
    }
2691
    case QUERY_NODE_LOGIC_PLAN_SORT: {
52✔
2692
      *keepSort = true;
52✔
2693
      NODES_CLEAR_LIST(*pSequencingNodes);
52✔
2694
      return TSDB_CODE_SUCCESS;
52✔
2695
    }
2696
    case QUERY_NODE_LOGIC_PLAN_JOIN: {
22,072✔
2697
      return sortPriKeyOptHandleJoinSort(pNode, groupSort, pSort, pNotOptimize, pSequencingNodes, keepSort);
22,072✔
2698
    }
2699
    case QUERY_NODE_LOGIC_PLAN_WINDOW: {
5,792✔
2700
      SWindowLogicNode* pWindowLogicNode = (SWindowLogicNode*)pNode;
5,792✔
2701
      // For interval window, we always apply sortPriKey optimization.
2702
      // For session/event/state window, the output ts order will always be ASC.
2703
      // If sort order is also asc, we apply optimization, otherwise we keep sort node to get correct output order.
2704
      if (pWindowLogicNode->winType == WINDOW_TYPE_INTERVAL || sortOrder == ORDER_ASC)
5,792✔
2705
        return nodesListMakeAppend(pSequencingNodes, (SNode*)pNode);
5,759✔
2706
    }
2707
    case QUERY_NODE_LOGIC_PLAN_AGG:
2708
    case QUERY_NODE_LOGIC_PLAN_PARTITION:
2709
    case QUERY_NODE_LOGIC_PLAN_DYN_QUERY_CTRL:
2710
      *pNotOptimize = true;
20,156✔
2711
      return TSDB_CODE_SUCCESS;
20,156✔
2712
    default:
22,971✔
2713
      break;
22,971✔
2714
  }
2715

2716
  if (1 != LIST_LENGTH(pNode->pChildren)) {
22,971!
2717
    *pNotOptimize = true;
5,168✔
2718
    return TSDB_CODE_SUCCESS;
5,168✔
2719
  }
2720

2721
  return sortPriKeyOptGetSequencingNodesImpl((SLogicNode*)nodesListGetNode(pNode->pChildren, 0), groupSort, pSort,
17,803✔
2722
                                             pNotOptimize, pSequencingNodes, keepSort);
2723
}
2724

2725
static int32_t sortPriKeyOptGetSequencingNodes(SSortLogicNode* pSort, bool groupSort, SNodeList** pSequencingNodes,
152,897✔
2726
                                               bool* keepSort) {
2727
  bool    notOptimize = false;
152,897✔
2728
  int32_t code = sortPriKeyOptGetSequencingNodesImpl((SLogicNode*)nodesListGetNode(pSort->node.pChildren, 0), groupSort,
152,897✔
2729
                                                     pSort, &notOptimize, pSequencingNodes, keepSort);
2730
  if (TSDB_CODE_SUCCESS != code || notOptimize) {
152,897!
2731
    NODES_CLEAR_LIST(*pSequencingNodes);
25,355✔
2732
  }
2733
  return code;
152,897✔
2734
}
2735

2736
static int32_t sortPriKeyOptApply(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan, SSortLogicNode* pSort,
122,585✔
2737
                                  SNodeList* pSequencingNodes) {
2738
  EOrder order = sortPriKeyOptGetPriKeyOrder(pSort);
122,585✔
2739
  SNode* pSequencingNode = NULL;
122,585✔
2740
  FOREACH(pSequencingNode, pSequencingNodes) {
267,205!
2741
    if (QUERY_NODE_LOGIC_PLAN_SCAN == nodeType(pSequencingNode)) {
144,618✔
2742
      SScanLogicNode* pScan = (SScanLogicNode*)pSequencingNode;
138,860✔
2743
      if ((ORDER_DESC == order && pScan->scanSeq[0] > 0) || (ORDER_ASC == order && pScan->scanSeq[1] > 0)) {
138,860✔
2744
        TSWAP(pScan->scanSeq[0], pScan->scanSeq[1]);
18,482✔
2745
      }
2746
      pScan->node.outputTsOrder = order;
138,860✔
2747
      if (TSDB_SUPER_TABLE == pScan->tableType) {
138,860✔
2748
        pScan->scanType = SCAN_TYPE_TABLE_MERGE;
65,084✔
2749
        pScan->filesetDelimited = true;
65,084✔
2750
        pScan->node.resultDataOrder = DATA_ORDER_LEVEL_GLOBAL;
65,084✔
2751
        pScan->node.requireDataOrder = DATA_ORDER_LEVEL_GLOBAL;
65,084✔
2752
      }
2753
      pScan->sortPrimaryKey = true;
138,860✔
2754
    } else if (QUERY_NODE_LOGIC_PLAN_WINDOW == nodeType(pSequencingNode)) {
5,758!
2755
      ((SLogicNode*)pSequencingNode)->outputTsOrder = order;
5,758✔
2756
    }
2757
    optSetParentOrder(((SLogicNode*)pSequencingNode)->pParent, order, (SLogicNode*)pSort);
144,618✔
2758
  }
2759

2760
  SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pSort->node.pChildren, 0);
122,587✔
2761
  if (NULL == pSort->node.pParent) {
122,587!
2762
    TSWAP(pSort->node.pTargets, pChild->pTargets);
×
2763
  }
2764
  int32_t code = replaceLogicNode(pLogicSubplan, (SLogicNode*)pSort, pChild);
122,587✔
2765
  if (TSDB_CODE_SUCCESS == code) {
122,587!
2766
    NODES_CLEAR_LIST(pSort->node.pChildren);
122,587✔
2767
    nodesDestroyNode((SNode*)pSort);
122,587✔
2768
  }
2769
  pCxt->optimized = true;
122,587✔
2770
  return code;
122,587✔
2771
}
2772

2773
static int32_t sortPrimaryKeyOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan, SSortLogicNode* pSort) {
152,898✔
2774
  SNodeList* pSequencingNodes = NULL;
152,898✔
2775
  bool       keepSort = true;
152,898✔
2776
  int32_t    code = sortPriKeyOptGetSequencingNodes(pSort, pSort->groupSort, &pSequencingNodes, &keepSort);
152,898✔
2777
  if (TSDB_CODE_SUCCESS == code) {
152,897!
2778
    if (pSequencingNodes != NULL) {
152,897✔
2779
      code = sortPriKeyOptApply(pCxt, pLogicSubplan, pSort, pSequencingNodes);
122,586✔
2780
    } else if (!keepSort) {
30,311!
2781
      SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pSort->node.pChildren, 0);
×
2782
      if (NULL == pSort->node.pParent) {
×
2783
        TSWAP(pSort->node.pTargets, pChild->pTargets);
×
2784
      }
2785
      int32_t code = replaceLogicNode(pLogicSubplan, (SLogicNode*)pSort, pChild);
×
2786
      if (TSDB_CODE_SUCCESS == code) {
×
2787
        NODES_CLEAR_LIST(pSort->node.pChildren);
×
2788
        nodesDestroyNode((SNode*)pSort);
×
2789
      }
2790
      pCxt->optimized = true;
×
2791
    } else {
2792
      // if we decided not to push down sort info to children, we should propagate output ts order to parents of pSort
2793
      optSetParentOrder(pSort->node.pParent, sortPriKeyOptGetPriKeyOrder(pSort), 0);
30,311✔
2794
      // we need to prevent this pSort from being chosen to do optimization again
2795
      pSort->skipPKSortOpt = true;
30,311✔
2796
      pCxt->optimized = true;
30,311✔
2797
    }
2798
  }
2799
  nodesClearList(pSequencingNodes);
152,898✔
2800
  return code;
152,898✔
2801
}
2802

2803
static int32_t sortPrimaryKeyOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
1,993,274✔
2804
  SSortLogicNode* pSort = (SSortLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, sortPriKeyOptMayBeOptimized, NULL);
1,993,274✔
2805
  if (NULL == pSort) {
1,993,361✔
2806
    return TSDB_CODE_SUCCESS;
1,840,466✔
2807
  }
2808
  return sortPrimaryKeyOptimizeImpl(pCxt, pLogicSubplan, pSort);
152,895✔
2809
}
2810

2811
static int32_t sortForJoinOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan, SJoinLogicNode* pJoin) {
88✔
2812
  SLogicNode*     pLeft = (SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 0);
88✔
2813
  SLogicNode*     pRight = (SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 1);
88✔
2814
  SScanLogicNode* pScan = NULL;
88✔
2815
  SLogicNode*     pChild = NULL;
88✔
2816
  SNode**         pChildPos = NULL;
88✔
2817
  EOrder          targetOrder = 0;
88✔
2818
  SSHashObj*      pTables = NULL;
88✔
2819

2820
  if (pJoin->node.inputTsOrder) {
88!
2821
    targetOrder = pJoin->node.inputTsOrder;
88✔
2822
    
2823
    if (pRight->outputTsOrder == pJoin->node.inputTsOrder) {
88✔
2824
      pChild = pLeft;
76✔
2825
      pChildPos = &pJoin->node.pChildren->pHead->pNode;
76✔
2826
    } else if (pLeft->outputTsOrder == pJoin->node.inputTsOrder) {
12!
2827
      pChild = pRight;
12✔
2828
      pChildPos = &pJoin->node.pChildren->pTail->pNode;
12✔
2829
    } else {
2830
      pChild = pRight;
×
2831
      pChildPos = &pJoin->node.pChildren->pTail->pNode;
×
2832
      targetOrder = pLeft->outputTsOrder;
×
2833
    }
2834
  } else {
2835
    if (QUERY_NODE_LOGIC_PLAN_SCAN == nodeType(pLeft) &&
×
2836
        !(((SScanLogicNode*)pLeft)->scanSeq[0] && ((SScanLogicNode*)pLeft)->scanSeq[1])){
×
2837
      pScan = (SScanLogicNode*)pLeft;
×
2838
      pChild = pRight;
×
2839
      pChildPos = &pJoin->node.pChildren->pTail->pNode;
×
2840
      targetOrder = pScan->node.outputTsOrder;
×
2841
    } else if (QUERY_NODE_LOGIC_PLAN_SCAN == nodeType(pRight) &&
×
2842
        !(((SScanLogicNode*)pRight)->scanSeq[0] && ((SScanLogicNode*)pRight)->scanSeq[1])) {
×
2843
      pScan = (SScanLogicNode*)pRight;
×
2844
      pChild = pLeft;
×
2845
      pChildPos = &pJoin->node.pChildren->pHead->pNode;
×
2846
      targetOrder = pScan->node.outputTsOrder;
×
2847
    } else {
2848
      pChild = pRight;
×
2849
      pChildPos = &pJoin->node.pChildren->pTail->pNode;
×
2850
      targetOrder = pLeft->outputTsOrder;
×
2851
    }
2852
    pJoin->node.inputTsOrder = targetOrder;
×
2853
  }
2854

2855
  if (QUERY_NODE_OPERATOR != nodeType(pJoin->pPrimKeyEqCond)) {
88!
2856
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
×
2857
  }
2858

2859
  bool           res = false;
88✔
2860
  SOperatorNode* pOp = (SOperatorNode*)pJoin->pPrimKeyEqCond;
88✔
2861

2862
  if ((QUERY_NODE_COLUMN != nodeType(pOp->pLeft) && QUERY_NODE_VALUE != nodeType(pOp->pLeft)) || 
88!
2863
      (QUERY_NODE_COLUMN != nodeType(pOp->pRight) && QUERY_NODE_VALUE != nodeType(pOp->pRight))) {
88!
2864
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
×
2865
  }
2866

2867
  SNode* pOrderByNode = NULL;
88✔
2868

2869
  int32_t code = collectTableAliasFromNodes((SNode*)pChild, &pTables);
88✔
2870
  if (TSDB_CODE_SUCCESS != code) {
88!
2871
    return code;
×
2872
  }
2873

2874
  char* opLeftTable = (QUERY_NODE_COLUMN == nodeType(pOp->pLeft)) ? ((SColumnNode*)pOp->pLeft)->tableAlias : ((SValueNode*)pOp->pLeft)->node.srcTable;
88✔
2875
  char* opRightTable = (QUERY_NODE_COLUMN == nodeType(pOp->pRight)) ? ((SColumnNode*)pOp->pRight)->tableAlias : ((SValueNode*)pOp->pRight)->node.srcTable;
88✔
2876
  
2877
  if (NULL != tSimpleHashGet(pTables, opLeftTable, strlen(opLeftTable))) {
88✔
2878
    pOrderByNode = pOp->pLeft;
80✔
2879
  } else if (NULL != tSimpleHashGet(pTables, opRightTable, strlen(opRightTable))) {
8!
2880
    pOrderByNode = pOp->pRight;
8✔
2881
  }
2882

2883
  tSimpleHashCleanup(pTables);
88✔
2884

2885
  if (NULL == pOrderByNode) {
88!
2886
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
×
2887
  }
2888

2889
  SSortLogicNode* pSort = NULL;
88✔
2890
  code = nodesMakeNode(QUERY_NODE_LOGIC_PLAN_SORT, (SNode**)&pSort);
88✔
2891
  if (NULL == pSort) {
88!
2892
    return code;
×
2893
  }
2894

2895
  pSort->node.outputTsOrder = targetOrder;
88✔
2896
  pSort->node.pTargets = NULL;
88✔
2897
  code = nodesCloneList(pChild->pTargets, &pSort->node.pTargets);
88✔
2898
  if (NULL == pSort->node.pTargets) {
88!
2899
    nodesDestroyNode((SNode*)pSort);
×
2900
    return code;
×
2901
  }
2902

2903
  pSort->groupSort = false;
88✔
2904
  SOrderByExprNode* pOrder = NULL;
88✔
2905
  code = nodesMakeNode(QUERY_NODE_ORDER_BY_EXPR, (SNode**)&pOrder);
88✔
2906
  if (NULL == pOrder) {
88!
2907
    nodesDestroyNode((SNode*)pSort);
×
2908
    return code;
×
2909
  }
2910

2911
  code = nodesListMakeStrictAppend(&pSort->pSortKeys, (SNode*)pOrder);
88✔
2912
  if (TSDB_CODE_SUCCESS != code) {
88!
2913
    nodesDestroyNode((SNode*)pSort);
×
2914
    return code;
×
2915
  }
2916
  pOrder->order = targetOrder;
88✔
2917
  pOrder->pExpr = NULL;
88✔
2918
  pOrder->nullOrder = (ORDER_ASC == pOrder->order) ? NULL_ORDER_FIRST : NULL_ORDER_LAST;
88✔
2919
  code = nodesCloneNode(pOrderByNode, &pOrder->pExpr);
88✔
2920
  if (!pOrder->pExpr) {
88!
2921
    nodesDestroyNode((SNode*)pSort);
×
2922
    return code;
×
2923
  }
2924

2925
  pChild->pParent = (SLogicNode*)pSort;
88✔
2926
  code = nodesListMakeAppend(&pSort->node.pChildren, (SNode*)pChild);
88✔
2927
  if (TSDB_CODE_SUCCESS != code) {
88!
2928
    return code;
×
2929
  }
2930
  *pChildPos = (SNode*)pSort;
88✔
2931
  pSort->node.pParent = (SLogicNode*)pJoin;
88✔
2932

2933
_return:
88✔
2934

2935
  pCxt->optimized = true;
88✔
2936

2937
  return TSDB_CODE_SUCCESS;
88✔
2938
}
2939

2940
static bool sortForJoinOptMayBeOptimized(SLogicNode* pNode, void* pCtx) {
7,587,517✔
2941
  if (QUERY_NODE_LOGIC_PLAN_JOIN != nodeType(pNode)) {
7,587,517✔
2942
    return false;
7,345,966✔
2943
  }
2944

2945
  SJoinLogicNode* pJoin = (SJoinLogicNode*)pNode;
241,551✔
2946
  if (pNode->pChildren->length != 2 || !pJoin->hasSubQuery || pJoin->isLowLevelJoin) {
241,551✔
2947
    return false;
233,545✔
2948
  }
2949

2950
  SLogicNode* pLeft = (SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 0);
8,006✔
2951
  SLogicNode* pRight = (SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 1);
8,006✔
2952

2953
  if (ORDER_ASC != pLeft->outputTsOrder && ORDER_DESC != pLeft->outputTsOrder) {
8,006✔
2954
    pLeft->outputTsOrder = ORDER_ASC;
424✔
2955
  }
2956
  if (ORDER_ASC != pRight->outputTsOrder && ORDER_DESC != pRight->outputTsOrder) {
8,006✔
2957
    pRight->outputTsOrder = ORDER_ASC;
312✔
2958
  }
2959

2960
  if (pLeft->outputTsOrder == pRight->outputTsOrder) {
8,006✔
2961
    return false;
7,918✔
2962
  }
2963

2964
  return true;
88✔
2965
}
2966

2967
static int32_t sortForJoinOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
1,840,367✔
2968
  SJoinLogicNode* pJoin =
2969
      (SJoinLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, sortForJoinOptMayBeOptimized, NULL);
1,840,367✔
2970
  if (NULL == pJoin) {
1,840,466✔
2971
    return TSDB_CODE_SUCCESS;
1,840,381✔
2972
  }
2973
  return sortForJoinOptimizeImpl(pCxt, pLogicSubplan, pJoin);
85✔
2974
}
2975

2976
static SScanLogicNode* joinCondGetScanNode(SLogicNode* pNode) {
279,501✔
2977
  switch (nodeType(pNode)) {
279,501!
2978
    case QUERY_NODE_LOGIC_PLAN_SCAN:
279,474✔
2979
      return (SScanLogicNode*)pNode;
279,474✔
2980
    case QUERY_NODE_LOGIC_PLAN_JOIN: {
27✔
2981
      SJoinLogicNode* pJoin = (SJoinLogicNode*)pNode;
27✔
2982
      if (JOIN_TYPE_INNER != pJoin->joinType) {
27✔
2983
        return NULL;
16✔
2984
      }
2985
      return joinCondGetScanNode((SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 0));
11✔
2986
    }
2987
    default:
×
2988
      return NULL;
×
2989
  }
2990
}
2991

2992
static int32_t joinCondGetAllScanNodes(SLogicNode* pNode, SNodeList** pList) {
14,935✔
2993
  switch (nodeType(pNode)) {
14,935!
2994
    case QUERY_NODE_LOGIC_PLAN_SCAN:
9,956✔
2995
      return nodesListMakeStrictAppend(pList, (SNode*)pNode);
9,956✔
2996
    case QUERY_NODE_LOGIC_PLAN_JOIN: {
4,979✔
2997
      SJoinLogicNode* pJoin = (SJoinLogicNode*)pNode;
4,979✔
2998
      if (JOIN_TYPE_INNER != pJoin->joinType) {
4,979!
2999
        return TSDB_CODE_SUCCESS;
×
3000
      }
3001
      int32_t code = joinCondGetAllScanNodes((SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 0), pList);
4,979✔
3002
      if (TSDB_CODE_SUCCESS == code) {
4,979!
3003
        code = joinCondGetAllScanNodes((SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 1), pList);
4,979✔
3004
      }
3005
      return code;
4,979✔
3006
    }
3007
    default:
×
3008
      return TSDB_CODE_SUCCESS;
×
3009
  }
3010
}
3011

3012
static bool joinCondMayBeOptimized(SLogicNode* pNode, void* pCtx) {
8,716,993✔
3013
  if (QUERY_NODE_LOGIC_PLAN_JOIN != nodeType(pNode) ||
8,716,993✔
3014
      OPTIMIZE_FLAG_TEST_MASK(pNode->optimizedFlag, OPTIMIZE_FLAG_JOIN_COND)) {
314,858✔
3015
    return false;
8,571,821✔
3016
  }
3017

3018
  SJoinLogicNode* pJoin = (SJoinLogicNode*)pNode;
145,172✔
3019
  if (pNode->pChildren->length != 2 || JOIN_STYPE_ASOF == pJoin->subType || JOIN_STYPE_WIN == pJoin->subType ||
145,172!
3020
      JOIN_TYPE_FULL == pJoin->joinType) {
143,539✔
3021
    OPTIMIZE_FLAG_SET_MASK(pNode->optimizedFlag, OPTIMIZE_FLAG_JOIN_COND);
1,970✔
3022
    return false;
1,970✔
3023
  }
3024

3025
  SLogicNode* pLeft = (SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 0);
143,202✔
3026
  SLogicNode* pRight = (SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 1);
143,202✔
3027

3028
  if ((JOIN_TYPE_LEFT == pJoin->joinType || JOIN_TYPE_RIGHT == pJoin->joinType) &&
143,202✔
3029
      (QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(pLeft) || QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(pRight))) {
2,426✔
3030
    OPTIMIZE_FLAG_SET_MASK(pNode->optimizedFlag, OPTIMIZE_FLAG_JOIN_COND);
909✔
3031
    return false;
909✔
3032
  }
3033

3034
  if (JOIN_TYPE_INNER == pJoin->joinType &&
142,293✔
3035
      ((QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(pLeft) && QUERY_NODE_LOGIC_PLAN_JOIN != nodeType(pLeft)) ||
140,776✔
3036
       (QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(pRight) && QUERY_NODE_LOGIC_PLAN_JOIN != nodeType(pRight)))) {
138,533✔
3037
    OPTIMIZE_FLAG_SET_MASK(pNode->optimizedFlag, OPTIMIZE_FLAG_JOIN_COND);
2,692✔
3038
    return false;
2,692✔
3039
  }
3040

3041
  if (JOIN_TYPE_INNER == pJoin->joinType) {
139,601✔
3042
    if (QUERY_NODE_LOGIC_PLAN_JOIN == nodeType(pLeft) &&
138,084✔
3043
        !OPTIMIZE_FLAG_TEST_MASK(pLeft->optimizedFlag, OPTIMIZE_FLAG_JOIN_COND)) {
95✔
3044
      return false;
72✔
3045
    }
3046
    if (QUERY_NODE_LOGIC_PLAN_JOIN == nodeType(pRight) &&
138,012✔
3047
        !OPTIMIZE_FLAG_TEST_MASK(pRight->optimizedFlag, OPTIMIZE_FLAG_JOIN_COND)) {
16✔
3048
      return false;
12✔
3049
    }
3050
  }
3051

3052
  SScanLogicNode* pLScan = joinCondGetScanNode(pLeft);
139,517✔
3053
  SScanLogicNode* pRScan = joinCondGetScanNode(pRight);
139,517✔
3054

3055
  if (NULL == pLScan || NULL == pRScan) {
139,517!
3056
    OPTIMIZE_FLAG_SET_MASK(pNode->optimizedFlag, OPTIMIZE_FLAG_JOIN_COND);
16✔
3057
    return false;
16✔
3058
  }
3059

3060
  if (!IS_TSWINDOW_SPECIFIED(pLScan->scanRange) && !IS_TSWINDOW_SPECIFIED(pRScan->scanRange)) {
139,501✔
3061
    OPTIMIZE_FLAG_SET_MASK(pNode->optimizedFlag, OPTIMIZE_FLAG_JOIN_COND);
134,291✔
3062
    return false;
134,291✔
3063
  }
3064

3065
  if (pJoin->pPrimKeyEqCond && QUERY_NODE_OPERATOR == nodeType(pJoin->pPrimKeyEqCond)) {
5,210!
3066
    SOperatorNode* pOp = (SOperatorNode*)pJoin->pPrimKeyEqCond;
4,426✔
3067
    if ((pOp->pLeft && QUERY_NODE_COLUMN != nodeType(pOp->pLeft)) || (pOp->pRight && QUERY_NODE_COLUMN != nodeType(pOp->pRight))) {
4,426!
3068
      return false;
5✔
3069
    }
3070
  }
3071

3072
  return true;
5,205✔
3073
}
3074

3075
static void joinCondMergeScanRange(STimeWindow* pDst, STimeWindow* pSrc) {
10,184✔
3076
  if (pSrc->skey > pDst->skey) {
10,184✔
3077
    pDst->skey = pSrc->skey;
210✔
3078
  }
3079
  if (pSrc->ekey < pDst->ekey) {
10,184✔
3080
    pDst->ekey = pSrc->ekey;
5,053✔
3081
  }
3082
}
10,184✔
3083

3084
static int32_t joinCondOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
2,017,954✔
3085
  SJoinLogicNode* pJoin = (SJoinLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, joinCondMayBeOptimized, NULL);
2,017,954✔
3086
  if (NULL == pJoin) {
2,018,051✔
3087
    return TSDB_CODE_SUCCESS;
2,012,847✔
3088
  }
3089

3090
  switch (pJoin->joinType) {
5,204!
3091
    case JOIN_TYPE_INNER: {
4,977✔
3092
      SNodeList* pScanList = NULL;
4,977✔
3093
      int32_t    code = joinCondGetAllScanNodes((SLogicNode*)pJoin, &pScanList);
4,977✔
3094
      if (TSDB_CODE_SUCCESS != code) {
4,977!
3095
        return code;
×
3096
      }
3097
      if (NULL == pScanList || pScanList->length <= 0) {
4,977!
3098
        nodesClearList(pScanList);
×
3099
        return TSDB_CODE_SUCCESS;
×
3100
      }
3101
      SNode*      pNode = NULL;
4,977✔
3102
      STimeWindow scanRange = TSWINDOW_INITIALIZER;
4,977✔
3103
      FOREACH(pNode, pScanList) { joinCondMergeScanRange(&scanRange, &((SScanLogicNode*)pNode)->scanRange); }
14,933!
3104
      FOREACH(pNode, pScanList) {
14,933!
3105
        ((SScanLogicNode*)pNode)->scanRange.skey = scanRange.skey;
9,956✔
3106
        ((SScanLogicNode*)pNode)->scanRange.ekey = scanRange.ekey;
9,956✔
3107
      }
3108
      nodesClearList(pScanList);
4,977✔
3109
      break;
4,977✔
3110
    }
3111
    case JOIN_TYPE_LEFT: {
117✔
3112
      SLogicNode*     pLeft = (SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 0);
117✔
3113
      SLogicNode*     pRight = (SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 1);
117✔
3114
      SScanLogicNode* pLScan = joinCondGetScanNode(pLeft);
117✔
3115
      SScanLogicNode* pRScan = joinCondGetScanNode(pRight);
117✔
3116

3117
      if (NULL == pLScan || NULL == pRScan) {
117!
3118
        return TSDB_CODE_SUCCESS;
×
3119
      }
3120
      joinCondMergeScanRange(&pRScan->scanRange, &pLScan->scanRange);
117✔
3121
      break;
117✔
3122
    }
3123
    case JOIN_TYPE_RIGHT: {
111✔
3124
      SLogicNode*     pLeft = (SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 0);
111✔
3125
      SLogicNode*     pRight = (SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 1);
111✔
3126
      SScanLogicNode* pLScan = joinCondGetScanNode(pLeft);
111✔
3127
      SScanLogicNode* pRScan = joinCondGetScanNode(pRight);
111✔
3128

3129
      if (NULL == pLScan || NULL == pRScan) {
111!
3130
        return TSDB_CODE_SUCCESS;
×
3131
      }
3132
      joinCondMergeScanRange(&pLScan->scanRange, &pRScan->scanRange);
111✔
3133
      break;
111✔
3134
    }
UNCOV
3135
    default:
×
UNCOV
3136
      return TSDB_CODE_SUCCESS;
×
3137
  }
3138

3139
  OPTIMIZE_FLAG_SET_MASK(pJoin->node.optimizedFlag, OPTIMIZE_FLAG_JOIN_COND);
5,205✔
3140

3141
  pCxt->optimized = true;
5,205✔
3142

3143
  return TSDB_CODE_SUCCESS;
5,205✔
3144
}
3145

3146
static bool smaIndexOptMayBeOptimized(SLogicNode* pNode, void* pCtx) {
7,587,323✔
3147
  if (QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(pNode) || NULL == pNode->pParent ||
7,587,323✔
3148
      QUERY_NODE_LOGIC_PLAN_WINDOW != nodeType(pNode->pParent) ||
2,290,431✔
3149
      WINDOW_TYPE_INTERVAL != ((SWindowLogicNode*)pNode->pParent)->winType) {
56,669✔
3150
    return false;
7,546,730✔
3151
  }
3152

3153
  SScanLogicNode* pScan = (SScanLogicNode*)pNode;
40,593✔
3154
  if (NULL == pScan->pSmaIndexes || NULL != pScan->node.pConditions) {
40,593!
3155
    return false;
40,593✔
3156
  }
3157

3158
  return true;
×
3159
}
3160

3161
static int32_t smaIndexOptCreateSmaScan(SScanLogicNode* pScan, STableIndexInfo* pIndex, SNodeList* pCols,
×
3162
                                        SLogicNode** pOutput) {
3163
  SScanLogicNode* pSmaScan = NULL;
×
3164
  int32_t         code = nodesMakeNode(QUERY_NODE_LOGIC_PLAN_SCAN, (SNode**)&pSmaScan);
×
3165
  if (NULL == pSmaScan) {
×
3166
    return code;
×
3167
  }
3168
  pSmaScan->pScanCols = pCols;
×
3169
  pSmaScan->tableType = TSDB_SUPER_TABLE;
×
3170
  pSmaScan->tableId = pIndex->dstTbUid;
×
3171
  pSmaScan->stableId = pIndex->dstTbUid;
×
3172
  pSmaScan->scanType = SCAN_TYPE_TABLE;
×
3173
  pSmaScan->scanSeq[0] = pScan->scanSeq[0];
×
3174
  pSmaScan->scanSeq[1] = pScan->scanSeq[1];
×
3175
  pSmaScan->scanRange = pScan->scanRange;
×
3176
  pSmaScan->dataRequired = FUNC_DATA_REQUIRED_DATA_LOAD;
×
3177

3178
  pSmaScan->pVgroupList = taosMemoryCalloc(1, sizeof(SVgroupsInfo) + sizeof(SVgroupInfo));
×
3179
  if (!pSmaScan->pVgroupList) {
×
3180
    nodesDestroyNode((SNode*)pSmaScan);
×
3181
    return terrno;
×
3182
  }
3183
  code = nodesCloneList(pCols, &pSmaScan->node.pTargets);
×
3184
  if (NULL == pSmaScan->node.pTargets) {
×
3185
    nodesDestroyNode((SNode*)pSmaScan);
×
3186
    return code;
×
3187
  }
3188
  pSmaScan->pVgroupList->numOfVgroups = 1;
×
3189
  pSmaScan->pVgroupList->vgroups[0].vgId = pIndex->dstVgId;
×
3190
  memcpy(&(pSmaScan->pVgroupList->vgroups[0].epSet), &pIndex->epSet, sizeof(SEpSet));
×
3191

3192
  *pOutput = (SLogicNode*)pSmaScan;
×
3193
  return TSDB_CODE_SUCCESS;
×
3194
}
3195

3196
static bool smaIndexOptEqualInterval(SScanLogicNode* pScan, SWindowLogicNode* pWindow, STableIndexInfo* pIndex, void* tz) {
×
3197
  if (pWindow->interval != pIndex->interval || pWindow->intervalUnit != pIndex->intervalUnit ||
×
3198
      pWindow->offset != pIndex->offset || pWindow->sliding != pIndex->sliding ||
×
3199
      pWindow->slidingUnit != pIndex->slidingUnit) {
×
3200
    return false;
×
3201
  }
3202
  if (IS_TSWINDOW_SPECIFIED(pScan->scanRange)) {
×
3203
    SInterval interval = {.interval = pIndex->interval,
×
3204
                          .intervalUnit = pIndex->intervalUnit,
×
3205
                          .offset = pIndex->offset,
×
3206
                          .offsetUnit = TIME_UNIT_MILLISECOND,
3207
                          .sliding = pIndex->sliding,
×
3208
                          .slidingUnit = pIndex->slidingUnit,
×
3209
                          .timezone = tz,
3210
                          .precision = pScan->node.precision};
×
3211
    return (pScan->scanRange.skey == taosTimeTruncate(pScan->scanRange.skey, &interval)) &&
×
3212
           (pScan->scanRange.ekey + 1 == taosTimeTruncate(pScan->scanRange.ekey + 1, &interval));
×
3213
  }
3214
  return true;
×
3215
}
3216

3217
static int32_t smaIndexOptCreateSmaCol(SNode* pFunc, uint64_t tableId, int32_t colId, SColumnNode** ppNode) {
×
3218
  SColumnNode* pCol = NULL;
×
3219
  int32_t      code = nodesMakeNode(QUERY_NODE_COLUMN, (SNode**)&pCol);
×
3220
  if (NULL == pCol) {
×
3221
    return code;
×
3222
  }
3223
  pCol->tableId = tableId;
×
3224
  pCol->tableType = TSDB_SUPER_TABLE;
×
3225
  pCol->colId = colId;
×
3226
  pCol->colType = COLUMN_TYPE_COLUMN;
×
3227
  tstrncpy(pCol->colName, ((SExprNode*)pFunc)->aliasName, TSDB_COL_NAME_LEN);
×
3228
  pCol->node.resType = ((SExprNode*)pFunc)->resType;
×
3229
  tstrncpy(pCol->node.aliasName, ((SExprNode*)pFunc)->aliasName, TSDB_COL_NAME_LEN);
×
3230
  *ppNode = pCol;
×
3231
  return code;
×
3232
}
3233

3234
static int32_t smaIndexOptFindSmaFunc(SNode* pQueryFunc, SNodeList* pSmaFuncs) {
×
3235
  int32_t index = 0;
×
3236
  SNode*  pSmaFunc = NULL;
×
3237
  FOREACH(pSmaFunc, pSmaFuncs) {
×
3238
    if (nodesEqualNode(pQueryFunc, pSmaFunc)) {
×
3239
      return index;
×
3240
    }
3241
    ++index;
×
3242
  }
3243
  return -1;
×
3244
}
3245

3246
static SNode* smaIndexOptFindWStartFunc(SNodeList* pSmaFuncs) {
×
3247
  SNode* pSmaFunc = NULL;
×
3248
  FOREACH(pSmaFunc, pSmaFuncs) {
×
3249
    if (QUERY_NODE_FUNCTION == nodeType(pSmaFunc) && FUNCTION_TYPE_WSTART == ((SFunctionNode*)pSmaFunc)->funcType) {
×
3250
      return pSmaFunc;
×
3251
    }
3252
  }
3253
  return NULL;
×
3254
}
3255

3256
static int32_t smaIndexOptCreateSmaCols(SNodeList* pFuncs, uint64_t tableId, SNodeList* pSmaFuncs,
×
3257
                                        SNodeList** pOutput) {
3258
  SNodeList* pCols = NULL;
×
3259
  SNode*     pFunc = NULL;
×
3260
  int32_t    code = TSDB_CODE_SUCCESS;
×
3261
  int32_t    index = 0;
×
3262
  int32_t    smaFuncIndex = -1;
×
3263
  bool       hasWStart = false;
×
3264
  FOREACH(pFunc, pFuncs) {
×
3265
    smaFuncIndex = smaIndexOptFindSmaFunc(pFunc, pSmaFuncs);
×
3266
    if (smaFuncIndex < 0) {
×
3267
      break;
×
3268
    } else {
3269
      SColumnNode* pCol = NULL;
×
3270
      code = smaIndexOptCreateSmaCol(pFunc, tableId, smaFuncIndex + 1, &pCol);
×
3271
      if (TSDB_CODE_SUCCESS != code) {
×
3272
        break;
×
3273
      }
3274
      code = nodesListMakeStrictAppend(&pCols, (SNode*)pCol);
×
3275
      if (TSDB_CODE_SUCCESS != code) {
×
3276
        break;
×
3277
      }
3278
      if (!hasWStart) {
×
3279
        if (PRIMARYKEY_TIMESTAMP_COL_ID == ((SColumnNode*)pCols->pTail->pNode)->colId) {
×
3280
          hasWStart = true;
×
3281
        }
3282
      }
3283
    }
3284
    ++index;
×
3285
  }
3286

3287
  if (TSDB_CODE_SUCCESS == code && smaFuncIndex >= 0) {
×
3288
    if (!hasWStart) {
×
3289
      SNode* pWsNode = smaIndexOptFindWStartFunc(pSmaFuncs);
×
3290
      if (!pWsNode) {
×
3291
        nodesDestroyList(pCols);
×
3292
        code = TSDB_CODE_APP_ERROR;
×
3293
        qError("create sma cols failed since %s(_wstart not exist)", tstrerror(code));
×
3294
        return code;
×
3295
      }
3296
      SExprNode exprNode;
3297
      exprNode.resType = ((SExprNode*)pWsNode)->resType;
×
3298
      rewriteExprAliasName(&exprNode, index + 1);
×
3299
      SColumnNode* pkNode = NULL;
×
3300
      code = smaIndexOptCreateSmaCol((SNode*)&exprNode, tableId, PRIMARYKEY_TIMESTAMP_COL_ID, &pkNode);
×
3301
      if (TSDB_CODE_SUCCESS != code) {
×
3302
        nodesDestroyList(pCols);
×
3303
        return code;
×
3304
      }
3305
      code = nodesListPushFront(pCols, (SNode*)pkNode);
×
3306
      if (TSDB_CODE_SUCCESS != code) {
×
3307
        nodesDestroyNode((SNode*)pkNode);
×
3308
        nodesDestroyList(pCols);
×
3309
        return code;
×
3310
      }
3311
    }
3312
    *pOutput = pCols;
×
3313
  } else {
3314
    nodesDestroyList(pCols);
×
3315
  }
3316

3317
  return code;
×
3318
}
3319

3320
static int32_t smaIndexOptCouldApplyIndex(SScanLogicNode* pScan, STableIndexInfo* pIndex, SNodeList** pCols, void* tz) {
×
3321
  SWindowLogicNode* pWindow = (SWindowLogicNode*)pScan->node.pParent;
×
3322
  if (!smaIndexOptEqualInterval(pScan, pWindow, pIndex, tz)) {
×
3323
    return TSDB_CODE_SUCCESS;
×
3324
  }
3325
  SNodeList* pSmaFuncs = NULL;
×
3326
  int32_t    code = nodesStringToList(pIndex->expr, &pSmaFuncs);
×
3327
  if (TSDB_CODE_SUCCESS == code) {
×
3328
    code = smaIndexOptCreateSmaCols(pWindow->pFuncs, pIndex->dstTbUid, pSmaFuncs, pCols);
×
3329
  }
3330
  nodesDestroyList(pSmaFuncs);
×
3331
  return code;
×
3332
}
3333

3334
static int32_t smaIndexOptApplyIndex(SLogicSubplan* pLogicSubplan, SScanLogicNode* pScan, STableIndexInfo* pIndex,
×
3335
                                     SNodeList* pSmaCols) {
3336
  SLogicNode* pSmaScan = NULL;
×
3337
  int32_t     code = smaIndexOptCreateSmaScan(pScan, pIndex, pSmaCols, &pSmaScan);
×
3338
  if (TSDB_CODE_SUCCESS == code) {
×
3339
    code = replaceLogicNode(pLogicSubplan, pScan->node.pParent, pSmaScan);
×
3340
  }
3341
  if (TSDB_CODE_SUCCESS == code) {
×
3342
    nodesDestroyNode((SNode*)pScan->node.pParent);
×
3343
  }
3344
  return code;
×
3345
}
3346

3347
static int32_t smaIndexOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan, SScanLogicNode* pScan) {
×
3348
  int32_t code = TSDB_CODE_SUCCESS;
×
3349
  int32_t nindexes = taosArrayGetSize(pScan->pSmaIndexes);
×
3350
  for (int32_t i = 0; i < nindexes; ++i) {
×
3351
    STableIndexInfo* pIndex = taosArrayGet(pScan->pSmaIndexes, i);
×
3352
    SNodeList*       pSmaCols = NULL;
×
3353
    code = smaIndexOptCouldApplyIndex(pScan, pIndex, &pSmaCols, pCxt->pPlanCxt->timezone);
×
3354
    if (TSDB_CODE_SUCCESS == code && NULL != pSmaCols) {
×
3355
      code = smaIndexOptApplyIndex(pLogicSubplan, pScan, pIndex, pSmaCols);
×
3356
      pCxt->optimized = true;
×
3357
      break;
×
3358
    }
3359
  }
3360
  return code;
×
3361
}
3362

3363
static int32_t smaIndexOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
1,840,282✔
3364
  SScanLogicNode* pScan = (SScanLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, smaIndexOptMayBeOptimized, NULL);
1,840,282✔
3365
  if (NULL == pScan) {
1,840,356!
3366
    return TSDB_CODE_SUCCESS;
1,840,359✔
3367
  }
3368
  return smaIndexOptimizeImpl(pCxt, pLogicSubplan, pScan);
×
3369
}
3370

3371
static EDealRes partTagsOptHasTbname(SNode* pNode, void* pContext) {
26,880✔
3372
  if (QUERY_NODE_COLUMN == nodeType(pNode)) {
26,880✔
3373
    if (COLUMN_TYPE_TBNAME == ((SColumnNode*)pNode)->colType) {
1,340✔
3374
      *(bool*)pContext = true;
333✔
3375
      return DEAL_RES_END;
333✔
3376
    }
3377
  }
3378
  return DEAL_RES_CONTINUE;
26,547✔
3379
}
3380

3381
static bool planOptNodeListHasTbname(SNodeList* pKeys) {
13,186✔
3382
  bool hasCol = false;
13,186✔
3383
  nodesWalkExprs(pKeys, partTagsOptHasTbname, &hasCol);
13,186✔
3384
  return hasCol;
13,186✔
3385
}
3386

3387
static bool partTagsIsOptimizableNode(SLogicNode* pNode) {
6,807,938✔
3388
  bool ret = 1 == LIST_LENGTH(pNode->pChildren) &&
4,773,340✔
3389
             QUERY_NODE_LOGIC_PLAN_SCAN == nodeType(nodesListGetNode(pNode->pChildren, 0)) &&
13,264,952✔
3390
             SCAN_TYPE_TAG != ((SScanLogicNode*)nodesListGetNode(pNode->pChildren, 0))->scanType;
1,683,586✔
3391
  if (!ret) return ret;
6,808,058✔
3392
  switch (nodeType(pNode)) {
1,682,955✔
3393
    case QUERY_NODE_LOGIC_PLAN_PARTITION: {
83,721✔
3394
      if (pNode->pParent) {
83,721✔
3395
        if (nodeType(pNode->pParent) == QUERY_NODE_LOGIC_PLAN_WINDOW) {
83,717✔
3396
          SWindowLogicNode* pWindow = (SWindowLogicNode*)pNode->pParent;
15,913✔
3397
          if (pWindow->winType == WINDOW_TYPE_INTERVAL) {
15,913✔
3398
            // if interval has slimit, we push down partition node to scan, and scan will set groupOrderScan to true
3399
            //   we want to skip groups of blocks after slimit satisfied
3400
            // if interval only has limit, we do not push down partition node to scan
3401
            //   we want to get grouped output from partition node and make use of limit
3402
            // if no slimit and no limit, we push down partition node and groupOrderScan is false, cause we do not need
3403
            //   group ordered output
3404
            if (!pWindow->node.pSlimit && pWindow->node.pLimit) ret = false;
10,920✔
3405
          }
3406
        } else if (nodeType(pNode->pParent) == QUERY_NODE_LOGIC_PLAN_JOIN) {
67,804✔
3407
          ret = false;
1,636✔
3408
        }
3409
      }
3410
    } break;
83,721✔
3411
    case QUERY_NODE_LOGIC_PLAN_AGG: {
907,847✔
3412
      SAggLogicNode* pAgg = (SAggLogicNode*)pNode;
907,847✔
3413
      ret = pAgg->pGroupKeys && pAgg->pAggFuncs;
907,847✔
3414
    } break;
907,847✔
3415
    default:
691,387✔
3416
      ret = false;
691,387✔
3417
      break;
691,387✔
3418
  }
3419
  return ret;
1,682,955✔
3420
}
3421

3422
static SNodeList* partTagsGetPartKeys(SLogicNode* pNode) {
158,676✔
3423
  if (QUERY_NODE_LOGIC_PLAN_PARTITION == nodeType(pNode)) {
158,676✔
3424
    return ((SPartitionLogicNode*)pNode)->pPartitionKeys;
80,842✔
3425
  } else {
3426
    return ((SAggLogicNode*)pNode)->pGroupKeys;
77,834✔
3427
  }
3428
}
3429

3430
static SNodeList* partTagsGetFuncs(SLogicNode* pNode) {
142,622✔
3431
  if (QUERY_NODE_LOGIC_PLAN_PARTITION == nodeType(pNode)) {
142,622✔
3432
    return NULL;
74,123✔
3433
  } else {
3434
    return ((SAggLogicNode*)pNode)->pAggFuncs;
68,499✔
3435
  }
3436
}
3437

3438
static bool partTagsOptAreSupportedFuncs(SNodeList* pFuncs) {
142,622✔
3439
  SNode* pFunc = NULL;
142,622✔
3440
  FOREACH(pFunc, pFuncs) {
318,759✔
3441
    if (fmIsIndefiniteRowsFunc(((SFunctionNode*)pFunc)->funcId) && !fmIsSelectFunc(((SFunctionNode*)pFunc)->funcId)) {
176,137!
3442
      return false;
×
3443
    }
3444
  }
3445
  return true;
142,622✔
3446
}
3447

3448
static bool partTagsOptMayBeOptimized(SLogicNode* pNode, void* pCtx) {
6,807,942✔
3449
  if (!partTagsIsOptimizableNode(pNode)) {
6,807,942✔
3450
    return false;
6,649,311✔
3451
  }
3452

3453
  return !keysHasCol(partTagsGetPartKeys(pNode)) && partTagsOptAreSupportedFuncs(partTagsGetFuncs(pNode));
158,639!
3454
}
3455

3456
static int32_t partTagsOptRebuildTbanme(SNodeList* pPartKeys) {
142,622✔
3457
  int32_t code = TSDB_CODE_SUCCESS;
142,622✔
3458
  nodesRewriteExprs(pPartKeys, optRebuildTbanme, &code);
142,622✔
3459
  return code;
142,622✔
3460
}
3461

3462
// todo refact: just to mask compilation warnings
3463
static void partTagsSetAlias(char* pAlias, const char* pTableAlias, const char* pColName) {
8,169✔
3464
  char    name[TSDB_COL_FNAME_LEN + 1] = {0};
8,169✔
3465
  int32_t len = tsnprintf(name, TSDB_COL_FNAME_LEN, "%s.%s", pTableAlias, pColName);
8,169✔
3466

3467
  (void)taosHashBinary(name, len);
3468
  tstrncpy(pAlias, name, TSDB_COL_NAME_LEN);
8,169✔
3469
}
8,169✔
3470

3471
static int32_t partTagsCreateWrapperFunc(const char* pFuncName, SNode* pNode, SFunctionNode** ppNode) {
50,290✔
3472
  SNode*         pNew = NULL;
50,290✔
3473
  SFunctionNode* pFunc = NULL;
50,290✔
3474
  int32_t        code = nodesMakeNode(QUERY_NODE_FUNCTION, (SNode**)&pFunc);
50,290✔
3475
  if (NULL == pFunc) {
50,290!
3476
    return code;
×
3477
  }
3478

3479
  snprintf(pFunc->functionName, sizeof(pFunc->functionName), "%s", pFuncName);
50,290✔
3480
  if ((QUERY_NODE_COLUMN == nodeType(pNode) && COLUMN_TYPE_TBNAME != ((SColumnNode*)pNode)->colType) ||
50,290✔
3481
      (QUERY_NODE_COLUMN == nodeType(pNode) && COLUMN_TYPE_TBNAME == ((SColumnNode*)pNode)->colType &&
43,213!
3482
       ((SColumnNode*)pNode)->tableAlias[0] != '\0')) {
51,310✔
3483
    SColumnNode* pCol = (SColumnNode*)pNode;
8,169✔
3484
    partTagsSetAlias(pFunc->node.aliasName, pCol->tableAlias, pCol->colName);
8,169✔
3485
  } else {
3486
    tstrncpy(pFunc->node.aliasName, ((SExprNode*)pNode)->aliasName, TSDB_COL_NAME_LEN);
42,121✔
3487
  }
3488
  code = nodesCloneNode(pNode, &pNew);
50,290✔
3489
  if (TSDB_CODE_SUCCESS == code) {
50,290!
3490
    code = nodesListMakeStrictAppend(&pFunc->pParameterList, pNew);
50,290✔
3491
  }
3492
  if (TSDB_CODE_SUCCESS == code) {
50,290!
3493
    code = fmGetFuncInfo(pFunc, NULL, 0);
50,290✔
3494
  }
3495

3496
  if (TSDB_CODE_SUCCESS != code) {
50,290!
3497
    nodesDestroyNode((SNode*)pFunc);
×
3498
    return code;
×
3499
  }
3500
  *ppNode = pFunc;
50,290✔
3501
  return code;
50,290✔
3502
}
3503

3504
static bool partTagsHasIndefRowsSelectFunc(SNodeList* pFuncs) {
68,499✔
3505
  SNode* pFunc = NULL;
68,499✔
3506
  FOREACH(pFunc, pFuncs) {
244,636!
3507
    if (fmIsIndefiniteRowsFunc(((SFunctionNode*)pFunc)->funcId)) {
176,137!
3508
      return true;
×
3509
    }
3510
  }
3511
  return false;
68,499✔
3512
}
3513

3514
static bool partTagsNeedOutput(SNode* pExpr, SNodeList* pTargets) {
73,275✔
3515
  SNode* pOutput = NULL;
73,275✔
3516
  FOREACH(pOutput, pTargets) {
299,349!
3517
    if (QUERY_NODE_COLUMN == nodeType(pExpr)) {
276,364✔
3518
      if (nodesEqualNode(pExpr, pOutput)) {
275,998✔
3519
        return true;
50,218✔
3520
      }
3521
    } else if (0 == strcmp(((SExprNode*)pExpr)->aliasName, ((SColumnNode*)pOutput)->colName)) {
366✔
3522
      return true;
72✔
3523
    }
3524
  }
3525
  return false;
22,985✔
3526
}
3527

3528
static int32_t partTagsRewriteGroupTagsToFuncs(SNodeList* pGroupTags, int32_t start, SAggLogicNode* pAgg) {
68,499✔
3529
  bool    hasIndefRowsSelectFunc = partTagsHasIndefRowsSelectFunc(pAgg->pAggFuncs);
68,499✔
3530
  int32_t code = TSDB_CODE_SUCCESS;
68,499✔
3531
  int32_t index = 0;
68,499✔
3532
  SNode*  pNode = NULL;
68,499✔
3533
  FOREACH(pNode, pGroupTags) {
141,775!
3534
    if (index++ < start || !partTagsNeedOutput(pNode, pAgg->node.pTargets)) {
73,276✔
3535
      continue;
22,986✔
3536
    }
3537
    SFunctionNode* pFunc = NULL;
50,290✔
3538
    if (hasIndefRowsSelectFunc) {
50,290!
3539
      code = partTagsCreateWrapperFunc("_select_value", pNode, &pFunc);
×
3540
      if (TSDB_CODE_SUCCESS == code) {
×
3541
        code = nodesListStrictAppend(pAgg->pAggFuncs, (SNode*)pFunc);
×
3542
      }
3543
    } else {
3544
      code = partTagsCreateWrapperFunc("_group_key", pNode, &pFunc);
50,290✔
3545
      if (TSDB_CODE_SUCCESS == code) {
50,290!
3546
        code = nodesListStrictAppend(pAgg->pAggFuncs, (SNode*)pFunc);
50,290✔
3547
      }
3548
    }
3549
    if (TSDB_CODE_SUCCESS != code) {
50,290!
3550
      break;
×
3551
    }
3552
  }
3553
  return code;
68,499✔
3554
}
3555

3556
static EDealRes partTagsCollectColsNodes(SNode* pNode, void* pContext) {
99,372✔
3557
  SCollectColsCxt* pCxt = pContext;
99,372✔
3558
  if (QUERY_NODE_COLUMN == nodeType(pNode)) {
99,372✔
3559
    SColumnNode* pCol = (SColumnNode*)pNode;
28,403✔
3560
    if (NULL == taosHashGet(pCxt->pColHash, pCol->colName, strlen(pCol->colName))) {
28,403✔
3561
      pCxt->errCode = taosHashPut(pCxt->pColHash, pCol->colName, strlen(pCol->colName), NULL, 0);
19,290✔
3562
    }
3563
  }
3564

3565
  return (TSDB_CODE_SUCCESS == pCxt->errCode ? DEAL_RES_CONTINUE : DEAL_RES_ERROR);
99,372!
3566
}
3567

3568

3569
static bool partTagsIsScanPseudoColsInConds(SScanLogicNode* pScan) {
15,366✔
3570
  SCollectColsCxt cxt = {
15,366✔
3571
      .errCode = TSDB_CODE_SUCCESS,
3572
      .pColHash = taosHashInit(128, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_NO_LOCK)};
15,366✔
3573
  if (NULL == cxt.pColHash) {
15,366!
3574
    return true;
×
3575
  }
3576

3577
  nodesWalkExpr(pScan->node.pConditions, partTagsCollectColsNodes, &cxt);
15,366✔
3578
  if (cxt.errCode) {
15,366!
3579
    taosHashCleanup(cxt.pColHash);
×
3580
    return true;
×
3581
  }
3582

3583
  SNode* pNode = NULL;
15,366✔
3584
  FOREACH(pNode, pScan->pScanPseudoCols) {
47,692!
3585
    if (taosHashGet(cxt.pColHash, ((SExprNode*)pNode)->aliasName, strlen(((SExprNode*)pNode)->aliasName))) {
32,334✔
3586
      taosHashCleanup(cxt.pColHash);
8✔
3587
      return true;
8✔
3588
    }
3589
  }
3590

3591
  taosHashCleanup(cxt.pColHash);
15,358✔
3592
  return false;
15,358✔
3593
}
3594

3595
static int32_t partTagsOptRemovePseudoCols(SScanLogicNode* pScan) {
142,622✔
3596
  if (!pScan->noPseudoRefAfterGrp || NULL == pScan->pScanPseudoCols || pScan->pScanPseudoCols->length <= 0) {
142,622!
3597
    return TSDB_CODE_SUCCESS;
106,277✔
3598
  }
3599

3600
  if (pScan->node.pConditions && partTagsIsScanPseudoColsInConds(pScan)) {
36,345✔
3601
    return TSDB_CODE_SUCCESS;
8✔
3602
  }
3603

3604
  SNode* pNode = NULL, *pTarget = NULL;
36,337✔
3605
  FOREACH(pNode, pScan->pScanPseudoCols) {
90,776!
3606
    FOREACH(pTarget, pScan->node.pTargets) {
248,540!
3607
      if (0 == strcmp(((SExprNode*)pNode)->aliasName, ((SColumnNode*)pTarget)->colName)) {
248,539✔
3608
        ERASE_NODE(pScan->node.pTargets);
54,438✔
3609
        break;
54,438✔
3610
      }
3611
    }
3612
  }
3613
  
3614
  nodesDestroyList(pScan->pScanPseudoCols);
36,337✔
3615
  pScan->pScanPseudoCols = NULL;
36,337✔
3616

3617
  return TSDB_CODE_SUCCESS;
36,337✔
3618
}
3619

3620
static int32_t partTagsOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
1,755,428✔
3621
  SLogicNode* pNode = optFindPossibleNode(pLogicSubplan->pNode, partTagsOptMayBeOptimized, NULL);
1,755,428✔
3622
  if (NULL == pNode) {
1,755,538✔
3623
    return TSDB_CODE_SUCCESS;
1,612,919✔
3624
  }
3625

3626
  int32_t         code = TSDB_CODE_SUCCESS;
142,619✔
3627
  SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(pNode->pChildren, 0);
142,619✔
3628
  if (QUERY_NODE_LOGIC_PLAN_PARTITION == nodeType(pNode)) {
142,622✔
3629
    TSWAP(((SPartitionLogicNode*)pNode)->pPartitionKeys, pScan->pGroupTags);
74,123✔
3630
    TSWAP(((SPartitionLogicNode*)pNode)->pTags, pScan->pTags);
74,123✔
3631
    TSWAP(((SPartitionLogicNode*)pNode)->pSubtable, pScan->pSubtable);
74,123✔
3632
    int32_t code = replaceLogicNode(pLogicSubplan, pNode, (SLogicNode*)pScan);
74,123✔
3633
    if (TSDB_CODE_SUCCESS == code) {
74,123!
3634
      code = adjustLogicNodeDataRequirement((SLogicNode*)pScan, pNode->resultDataOrder);
74,123✔
3635
    }
3636
    if (TSDB_CODE_SUCCESS == code) {
74,123!
3637
      if (QUERY_NODE_LOGIC_PLAN_AGG == pNode->pParent->type) {
74,123✔
3638
        SAggLogicNode* pParent = (SAggLogicNode*)(pNode->pParent);
50,998✔
3639
        scanPathOptSetGroupOrderScan(pScan);
50,998✔
3640
        pParent->hasGroupKeyOptimized = true;
50,998✔
3641
      }
3642
      if (pNode->pParent->pSlimit) pScan->groupOrderScan = true;
74,123✔
3643

3644
      NODES_CLEAR_LIST(pNode->pChildren);
74,123✔
3645
      nodesDestroyNode((SNode*)pNode);
74,123✔
3646
    }
3647
  } else {
3648
    SAggLogicNode* pAgg = (SAggLogicNode*)pNode;
68,499✔
3649
    int32_t        start = -1;
68,499✔
3650
    SNode*         pGroupKey = NULL;
68,499✔
3651
    FOREACH(pGroupKey, pAgg->pGroupKeys) {
141,774!
3652
      SNode* pGroupExpr = nodesListGetNode(((SGroupingSetNode*)pGroupKey)->pParameterList, 0);
73,275✔
3653
      if (NULL != pScan->pGroupTags) {
73,275✔
3654
        SNode* pGroupTag = NULL;
4,777✔
3655
        FOREACH(pGroupTag, pScan->pGroupTags) {
10,294!
3656
          if (nodesEqualNode(pGroupTag, pGroupExpr)) {
5,517!
3657
            continue;
×
3658
          }
3659
        }
3660
      }
3661
      if (start < 0) {
73,275✔
3662
        start = LIST_LENGTH(pScan->pGroupTags);
68,499✔
3663
      }
3664
      SNode* pNew = NULL;
73,275✔
3665
      code = nodesCloneNode(pGroupExpr, &pNew);
73,275✔
3666
      if (TSDB_CODE_SUCCESS == code) {
73,275!
3667
        code = nodesListMakeStrictAppend(&pScan->pGroupTags, pNew);
73,275✔
3668
      }
3669
      if (TSDB_CODE_SUCCESS != code) {
73,275!
3670
        break;
×
3671
      }
3672
    }
3673
    pAgg->hasGroupKeyOptimized = true;
68,499✔
3674

3675
    NODES_DESTORY_LIST(pAgg->pGroupKeys);
68,499✔
3676
    if (TSDB_CODE_SUCCESS == code && start >= 0) {
68,499!
3677
      code = partTagsRewriteGroupTagsToFuncs(pScan->pGroupTags, start, pAgg);
68,499✔
3678
    }
3679
  }
3680
  if (TSDB_CODE_SUCCESS == code) {
142,622!
3681
    code = partTagsOptRemovePseudoCols(pScan);
142,622✔
3682
  }
3683
  if (TSDB_CODE_SUCCESS == code) {
142,622!
3684
    code = partTagsOptRebuildTbanme(pScan->pGroupTags);
142,622✔
3685
  }
3686

3687
  pCxt->optimized = true;
142,622✔
3688
  return code;
142,622✔
3689
}
3690

3691
static int32_t eliminateProjOptCheckProjColumnNames(SProjectLogicNode* pProjectNode, bool* pRet) {
502,260✔
3692
  int32_t   code = 0;
502,260✔
3693
  SHashObj* pProjColNameHash = taosHashInit(16, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_NO_LOCK);
502,260✔
3694
  SNode*    pProjection;
3695
  FOREACH(pProjection, pProjectNode->pProjections) {
2,344,605!
3696
    char*    projColumnName = ((SColumnNode*)pProjection)->colName;
1,850,934✔
3697
    int32_t* pExist = taosHashGet(pProjColNameHash, projColumnName, strlen(projColumnName));
1,850,934✔
3698
    if (NULL != pExist) {
1,850,895✔
3699
      taosHashCleanup(pProjColNameHash);
8,618✔
3700
      return false;
8,618✔
3701
    } else {
3702
      int32_t exist = 1;
1,842,277✔
3703
      code = taosHashPut(pProjColNameHash, projColumnName, strlen(projColumnName), &exist, sizeof(exist));
1,842,277✔
3704
      if (TSDB_CODE_SUCCESS != code) {
1,842,352!
3705
        break;
×
3706
      }
3707
    }
3708
  }
3709
  taosHashCleanup(pProjColNameHash);
493,671✔
3710
  *pRet = true;
493,655✔
3711
  return code;
493,655✔
3712
}
3713

3714
static bool eliminateProjOptMayBeOptimized(SLogicNode* pNode, void* pCtx) {
3,709,358✔
3715
  // Super table scan requires project operator to merge packets to improve performance.
3716
  if (NULL == pNode->pParent &&
3,709,358✔
3717
      (QUERY_NODE_LOGIC_PLAN_PROJECT != nodeType(pNode) || 1 != LIST_LENGTH(pNode->pChildren) ||
1,442,952✔
3718
       (QUERY_NODE_LOGIC_PLAN_SCAN == nodeType(nodesListGetNode(pNode->pChildren, 0)) &&
860,762✔
3719
        TSDB_SUPER_TABLE == ((SScanLogicNode*)nodesListGetNode(pNode->pChildren, 0))->tableType))) {
277,829✔
3720
    return false;
668,795✔
3721
  }
3722

3723
  if (OPTIMIZE_FLAG_TEST_MASK(pNode->optimizedFlag, OPTIMIZE_FLAG_ELIMINATE_PROJ)) {
3,040,644✔
3724
    return false;
13,901✔
3725
  }
3726

3727
  if (NULL != pNode->pParent &&
3,026,743✔
3728
      (QUERY_NODE_LOGIC_PLAN_PROJECT != nodeType(pNode) || 1 != LIST_LENGTH(pNode->pChildren) ||
2,266,463✔
3729
       QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(nodesListGetNode(pNode->pChildren, 0)) ||
315,514✔
3730
       QUERY_NODE_LOGIC_PLAN_JOIN != nodeType(pNode->pParent))) {
79,706✔
3731
    return false;
2,264,297✔
3732
  }
3733

3734
  if (QUERY_NODE_LOGIC_PLAN_PROJECT != nodeType(pNode) || 1 != LIST_LENGTH(pNode->pChildren)) {
762,446!
3735
    return false;
10✔
3736
  }
3737

3738
  if (QUERY_NODE_LOGIC_PLAN_DYN_QUERY_CTRL == nodeType(nodesListGetNode(pNode->pChildren, 0))) {
762,436✔
3739
    SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pNode->pChildren, 0);
115✔
3740
    if (LIST_LENGTH(pChild->pTargets) != LIST_LENGTH(pNode->pTargets)) {
109!
3741
      return false;
69✔
3742
    }
3743
    if (((SDynQueryCtrlLogicNode*)pChild)->qType == DYN_QTYPE_VTB_SCAN) {
40✔
3744
      return false;
14✔
3745
    }
3746
  }
3747

3748
  SProjectLogicNode* pProjectNode = (SProjectLogicNode*)pNode;
762,362✔
3749
  if (NULL != pProjectNode->node.pLimit || NULL != pProjectNode->node.pSlimit ||
762,362✔
3750
      NULL != pProjectNode->node.pConditions) {
713,708!
3751
    return false;
48,654✔
3752
  }
3753

3754
  SNode* pProjection;
3755
  FOREACH(pProjection, pProjectNode->pProjections) {
2,745,564!
3756
    SExprNode* pExprNode = (SExprNode*)pProjection;
2,243,304✔
3757
    if (QUERY_NODE_COLUMN != nodeType(pExprNode)) {
2,243,304✔
3758
      return false;
211,447✔
3759
    }
3760
    SColumnNode* pCol = (SColumnNode*)pExprNode;
2,031,857✔
3761
    if (NULL != pNode->pParent && 0 != strcmp(pCol->colName, pCol->node.aliasName)) {
2,031,857✔
3762
      return false;
1✔
3763
    }
3764
  }
3765
  int32_t* pCode = pCtx;
502,260✔
3766
  bool     ret = false;
502,260✔
3767
  int32_t  code = eliminateProjOptCheckProjColumnNames(pProjectNode, &ret);
502,260✔
3768
  if (TSDB_CODE_SUCCESS != code) {
502,274!
3769
    *pCode = code;
×
3770
  }
3771
  return ret;
502,274✔
3772
}
3773

3774
typedef struct CheckNewChildTargetsCxt {
3775
  SNodeList* pNewChildTargets;
3776
  bool       canUse;
3777
} CheckNewChildTargetsCxt;
3778

3779
static EDealRes eliminateProjOptCanUseNewChildTargetsImpl(SNode* pNode, void* pContext) {
126,275✔
3780
  if (QUERY_NODE_COLUMN == nodeType(pNode)) {
126,275✔
3781
    CheckNewChildTargetsCxt* pCxt = pContext;
43,424✔
3782
    SNode*                   pTarget = NULL;
43,424✔
3783
    FOREACH(pTarget, pCxt->pNewChildTargets) {
133,187✔
3784
      if (nodesEqualNode(pTarget, pNode)) {
122,466✔
3785
        pCxt->canUse = true;
32,699✔
3786
        return DEAL_RES_CONTINUE;
32,699✔
3787
      }
3788
    }
3789
    pCxt->canUse = false;
10,721✔
3790
    return DEAL_RES_END;
10,721✔
3791
  }
3792
  return DEAL_RES_CONTINUE;
82,851✔
3793
}
3794

3795
static bool eliminateProjOptCanChildConditionUseChildTargets(SLogicNode* pChild, SNodeList* pNewChildTargets) {
492,981✔
3796
  if (NULL != pChild->pConditions) {
492,981✔
3797
    CheckNewChildTargetsCxt cxt = {.pNewChildTargets = pNewChildTargets, .canUse = false};
32,891✔
3798
    nodesWalkExpr(pChild->pConditions, eliminateProjOptCanUseNewChildTargetsImpl, &cxt);
32,891✔
3799
    if (!cxt.canUse) return false;
32,890✔
3800
  }
3801

3802
  if (QUERY_NODE_LOGIC_PLAN_VIRTUAL_TABLE_SCAN == nodeType(pChild)) {
482,255✔
3803
    return false;
586✔
3804
  }
3805
  if (QUERY_NODE_LOGIC_PLAN_JOIN == nodeType(pChild) && ((SJoinLogicNode*)pChild)->joinAlgo != JOIN_ALGO_UNKNOWN) {
481,669!
3806
    return false;
2,086✔
3807
  }
3808
  if (QUERY_NODE_LOGIC_PLAN_JOIN == nodeType(pChild) && ((SJoinLogicNode*)pChild)->pFullOnCond) {
479,583!
3809
    SJoinLogicNode*         pJoinLogicNode = (SJoinLogicNode*)pChild;
×
3810
    CheckNewChildTargetsCxt cxt = {.pNewChildTargets = pNewChildTargets, .canUse = false};
×
3811
    nodesWalkExpr(pJoinLogicNode->pFullOnCond, eliminateProjOptCanUseNewChildTargetsImpl, &cxt);
×
3812
    if (!cxt.canUse) return false;
×
3813
  }
3814
  return true;
479,583✔
3815
}
3816

3817
static void alignProjectionWithTarget(SLogicNode* pNode) {
480,096✔
3818
  if (QUERY_NODE_LOGIC_PLAN_PROJECT != pNode->type) {
480,096✔
3819
    return;
474,793✔
3820
  }
3821

3822
  SProjectLogicNode* pProjectNode = (SProjectLogicNode*)pNode;
5,303✔
3823
  SNode*             pProjection = NULL;
5,303✔
3824
  FOREACH(pProjection, pProjectNode->pProjections) {
13,496!
3825
    SNode* pTarget = NULL;
8,193✔
3826
    bool   keep = false;
8,193✔
3827
    FOREACH(pTarget, pNode->pTargets) {
12,558!
3828
      if (0 == strcmp(((SColumnNode*)pProjection)->node.aliasName, ((SColumnNode*)pTarget)->colName)) {
12,482✔
3829
        keep = true;
8,117✔
3830
        break;
8,117✔
3831
      }
3832
    }
3833
    if (!keep) {
8,193✔
3834
      (void)nodesListErase(pProjectNode->pProjections, cell);
76✔
3835
    }
3836
  }
3837
}
3838

3839
typedef struct RewriteTableAliasCxt {
3840
  char* newTableAlias;
3841
  bool  rewriteColName;
3842
} RewriteTableAliasCxt;
3843

3844
static EDealRes eliminateProjOptRewriteScanTableAlias(SNode* pNode, void* pContext) {
3,008✔
3845
  if (QUERY_NODE_COLUMN == nodeType(pNode)) {
3,008✔
3846
    SColumnNode*          pCol = (SColumnNode*)pNode;
2,565✔
3847
    RewriteTableAliasCxt* pCtx = (RewriteTableAliasCxt*)pContext;
2,565✔
3848
    tstrncpy(pCol->tableAlias, pCtx->newTableAlias, TSDB_TABLE_NAME_LEN);
2,565✔
3849
  }
3850
  return DEAL_RES_CONTINUE;
3,008✔
3851
}
3852

3853
static void eliminateProjPushdownProjIdx(SNodeList* pParentProjects, SNodeList* pChildTargets) {
599✔
3854
  SNode *pChildTarget = NULL, *pParentProject = NULL;
599✔
3855
  FOREACH(pChildTarget, pChildTargets) {
1,789!
3856
    SColumnNode* pTargetCol = (SColumnNode*)pChildTarget;
1,190✔
3857
    FOREACH(pParentProject, pParentProjects) {
2,519!
3858
      SExprNode* pProject = (SExprNode*)pParentProject;
2,479✔
3859
      if (0 == strcmp(pTargetCol->colName, pProject->aliasName)) {
2,479✔
3860
        pTargetCol->resIdx = pProject->projIdx;
1,150✔
3861
        break;
1,150✔
3862
      }
3863
    }
3864
  }
3865
}
599✔
3866

3867
static int32_t eliminateProjOptFindProjPrefixWithOrderCheck(SProjectLogicNode* pProj, SProjectLogicNode* pChild,
221✔
3868
                                                            SNodeList** pNewChildTargets, bool* orderMatch) {
3869
  int32_t code = 0;
221✔
3870
  SNode * pProjection = NULL, *pChildTarget = NULL;
221✔
3871
  *orderMatch = true;
221✔
3872
  FORBOTH(pProjection, pProj->pProjections, pChildTarget, pChild->node.pTargets) {
626!
3873
    if (!pProjection) break;
437!
3874
    if (0 != strcmp(((SColumnNode*)pProjection)->colName, ((SColumnNode*)pChildTarget)->colName)) {
437✔
3875
      *orderMatch = false;
32✔
3876
      break;
32✔
3877
    }
3878
    if (pNewChildTargets) {
405✔
3879
      SNode* pNew = NULL;
220✔
3880
      code = nodesCloneNode(pChildTarget, &pNew);
220✔
3881
      if (TSDB_CODE_SUCCESS == code) {
220!
3882
        code = nodesListMakeStrictAppend(pNewChildTargets, pNew);
220✔
3883
      }
3884
      if (TSDB_CODE_SUCCESS != code && pNewChildTargets) {
220!
3885
        nodesDestroyList(*pNewChildTargets);
×
3886
        *pNewChildTargets = NULL;
×
3887
        break;
×
3888
      }
3889
    }
3890
  }
3891
  return code;
221✔
3892
}
3893

3894
static int32_t eliminateProjOptPushTargetsToSetOpChildren(SProjectLogicNode* pSetOp) {
20✔
3895
  SNode*  pChildProj = NULL;
20✔
3896
  int32_t code = 0;
20✔
3897
  bool    orderMatch = false;
20✔
3898
  FOREACH(pChildProj, pSetOp->node.pChildren) {
60!
3899
    if (QUERY_NODE_LOGIC_PLAN_PROJECT == nodeType(pChildProj)) {
40!
3900
      SProjectLogicNode* pChildLogic = (SProjectLogicNode*)pChildProj;
40✔
3901
      SNodeList*         pNewChildTargetsForChild = NULL;
40✔
3902
      code = eliminateProjOptFindProjPrefixWithOrderCheck(pSetOp, pChildLogic, &pNewChildTargetsForChild, &orderMatch);
40✔
3903
      if (TSDB_CODE_SUCCESS != code) break;
40!
3904
      nodesDestroyList(pChildLogic->node.pTargets);
40✔
3905
      pChildLogic->node.pTargets = pNewChildTargetsForChild;
40✔
3906
      alignProjectionWithTarget((SLogicNode*)pChildLogic);
40✔
3907
      if (pChildLogic->isSetOpProj) {
40✔
3908
        code = eliminateProjOptPushTargetsToSetOpChildren(pChildLogic);
16✔
3909
        if (TSDB_CODE_SUCCESS != code) break;
16!
3910
      }
3911
    }
3912
  }
3913
  return code;
20✔
3914
}
3915

3916
static int32_t eliminateProjOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan,
493,580✔
3917
                                         SProjectLogicNode* pProjectNode) {
3918
  SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pProjectNode->node.pChildren, 0);
493,580✔
3919
  int32_t     code = 0;
493,629✔
3920
  bool        isSetOpProj = false;
493,629✔
3921
  bool        orderMatch = false;
493,629✔
3922
  bool        sizeMatch = LIST_LENGTH(pProjectNode->pProjections) == LIST_LENGTH(pChild->pTargets);
493,629!
3923
  bool        needReplaceTargets = true;
493,629✔
3924

3925
  if (NULL == pProjectNode->node.pParent) {
493,629✔
3926
    SNodeList* pNewChildTargets = NULL;
493,053✔
3927
    SNode *    pProjection = NULL, *pChildTarget = NULL;
493,053✔
3928
    isSetOpProj = QUERY_NODE_LOGIC_PLAN_PROJECT == nodeType(pChild) && ((SProjectLogicNode*)pChild)->isSetOpProj;
493,053✔
3929
    if (isSetOpProj) {
493,053✔
3930
      // For sql: select ... from (select ... union all select ...);
3931
      // When eliminating the outer proj (the outer select), we have to make sure that the outer proj projections and
3932
      // union all project targets have same columns in the same order. See detail in TD-30188
3933
      code = eliminateProjOptFindProjPrefixWithOrderCheck(pProjectNode, (SProjectLogicNode*)pChild,
181✔
3934
                                                          sizeMatch ? NULL : &pNewChildTargets, &orderMatch);
3935
      if (TSDB_CODE_SUCCESS == code && sizeMatch && orderMatch) {
181!
3936
        pNewChildTargets = pChild->pTargets;
145✔
3937
        needReplaceTargets = false;
145✔
3938
      }
3939
    } else {
3940
      FOREACH(pProjection, pProjectNode->pProjections) {
2,292,541✔
3941
        FOREACH(pChildTarget, pChild->pTargets) {
1,203,483,947!
3942
          if (0 == strcmp(((SColumnNode*)pProjection)->colName, ((SColumnNode*)pChildTarget)->colName)) {
1,203,483,992✔
3943
            SNode* pNew = NULL;
1,799,716✔
3944
            code = nodesCloneNode(pChildTarget, &pNew);
1,799,716✔
3945
            if (TSDB_CODE_SUCCESS == code) {
1,799,697!
3946
              code = nodesListMakeStrictAppend(&pNewChildTargets, pNew);
1,799,701✔
3947
            }
3948
            break;
1,799,714✔
3949
          }
3950
        }
3951
        if (TSDB_CODE_SUCCESS != code) {
1,799,669!
3952
          break;
×
3953
        }
3954
      }
3955
    }
3956
    if (TSDB_CODE_SUCCESS != code) {
493,051!
3957
      nodesDestroyList(pNewChildTargets);
×
3958
      return code;
13,419✔
3959
    }
3960

3961
    if (eliminateProjOptCanChildConditionUseChildTargets(pChild, pNewChildTargets) && (!isSetOpProj || orderMatch)) {
493,051✔
3962
      if (needReplaceTargets) {
479,609✔
3963
        nodesDestroyList(pChild->pTargets);
479,458✔
3964
        pChild->pTargets = pNewChildTargets;
479,477✔
3965
      }
3966
    } else {
3967
      if (needReplaceTargets) nodesDestroyList(pNewChildTargets);
13,394!
3968
      OPTIMIZE_FLAG_SET_MASK(pProjectNode->node.optimizedFlag, OPTIMIZE_FLAG_ELIMINATE_PROJ);
13,419✔
3969
      pCxt->optimized = true;
13,419✔
3970
      return TSDB_CODE_SUCCESS;
13,419✔
3971
    }
3972
  } else {
3973
    RewriteTableAliasCxt cxt = {.newTableAlias = pProjectNode->stmtName, .rewriteColName = false};
576✔
3974
    SScanLogicNode*      pScan = (SScanLogicNode*)pChild;
576✔
3975
    nodesWalkExprs(pScan->pScanCols, eliminateProjOptRewriteScanTableAlias, &cxt);
576✔
3976
    nodesWalkExprs(pScan->pScanPseudoCols, eliminateProjOptRewriteScanTableAlias, &cxt);
599✔
3977
    nodesWalkExpr(pScan->node.pConditions, eliminateProjOptRewriteScanTableAlias, &cxt);
599✔
3978
    nodesWalkExprs(pChild->pTargets, eliminateProjOptRewriteScanTableAlias, &cxt);
599✔
3979
    eliminateProjPushdownProjIdx(pProjectNode->pProjections, pChild->pTargets);
599✔
3980
  }
3981

3982
  if (TSDB_CODE_SUCCESS == code) {
480,227✔
3983
    code = replaceLogicNode(pLogicSubplan, (SLogicNode*)pProjectNode, pChild);
480,189✔
3984
  }
3985
  if (TSDB_CODE_SUCCESS == code) {
480,209✔
3986
    if (pProjectNode->node.pHint && !pChild->pHint) TSWAP(pProjectNode->node.pHint, pChild->pHint);
480,193!
3987
    NODES_CLEAR_LIST(pProjectNode->node.pChildren);
480,193✔
3988
    nodesDestroyNode((SNode*)pProjectNode);
480,228✔
3989
    // if pChild is a project logic node, remove its projection which is not reference by its target.
3990
    if (needReplaceTargets) {
480,229✔
3991
      alignProjectionWithTarget(pChild);
480,085✔
3992
      // Since we have eliminated the outer proj, we need to push down the new targets to the children of the set
3993
      // operation.
3994
      if (isSetOpProj && orderMatch && !sizeMatch)
480,073!
3995
        code = eliminateProjOptPushTargetsToSetOpChildren((SProjectLogicNode*)pChild);
4✔
3996
    }
3997
  }
3998
  pCxt->optimized = true;
480,217✔
3999
  return code;
480,217✔
4000
}
4001

4002
static int32_t eliminateProjOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
1,442,928✔
4003
  int32_t            code = 0;
1,442,928✔
4004
  SProjectLogicNode* pProjectNode =
4005
      (SProjectLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, eliminateProjOptMayBeOptimized, &code);
1,442,928✔
4006

4007
  if (NULL == pProjectNode) {
1,442,999✔
4008
    return TSDB_CODE_SUCCESS;
949,360✔
4009
  }
4010

4011
  return eliminateProjOptimizeImpl(pCxt, pLogicSubplan, pProjectNode);
493,639✔
4012
}
4013

4014
static bool rewriteTailOptMayBeOptimized(SLogicNode* pNode, void* pCtx) {
5,237,353✔
4015
  return QUERY_NODE_LOGIC_PLAN_INDEF_ROWS_FUNC == nodeType(pNode) && ((SIndefRowsFuncLogicNode*)pNode)->isTailFunc;
5,237,353✔
4016
}
4017

4018
static int32_t rewriteTailOptCreateOrderByExpr(SNode* pSortKey, SNode** ppNode) {
11,293✔
4019
  SOrderByExprNode* pOrder = NULL;
11,293✔
4020
  int32_t           code = nodesMakeNode(QUERY_NODE_ORDER_BY_EXPR, (SNode**)&pOrder);
11,293✔
4021
  if (NULL == pOrder) {
11,293!
4022
    return code;
×
4023
  }
4024
  pOrder->order = ORDER_DESC;
11,293✔
4025
  pOrder->pExpr = NULL;
11,293✔
4026
  code = nodesCloneNode(pSortKey, &pOrder->pExpr);
11,293✔
4027
  if (NULL == pOrder->pExpr) {
11,293!
4028
    nodesDestroyNode((SNode*)pOrder);
×
4029
    return code;
×
4030
  }
4031
  *ppNode = (SNode*)pOrder;
11,293✔
4032
  return code;
11,293✔
4033
}
4034

4035
static int32_t rewriteTailOptCreateLimit(SNode* pLimit, SNode* pOffset, SNode** pOutput) {
11,293✔
4036
  SLimitNode* pLimitNode = NULL;
11,293✔
4037
  int32_t     code = nodesMakeNode(QUERY_NODE_LIMIT, (SNode**)&pLimitNode);
11,293✔
4038
  if (NULL == pLimitNode) {
11,293!
4039
    return code;
×
4040
  }
4041
  code = nodesMakeValueNodeFromInt64(NULL == pLimit ? -1 : ((SValueNode*)pLimit)->datum.i, (SNode**)&pLimitNode->limit);
11,293!
4042
  if (TSDB_CODE_SUCCESS != code) {
11,293!
4043
    return code;
×
4044
  }
4045
  code = nodesMakeValueNodeFromInt64(NULL == pOffset ? 0 : ((SValueNode*)pOffset)->datum.i, (SNode**)&pLimitNode->offset);
11,293✔
4046
  if (TSDB_CODE_SUCCESS != code) {
11,293!
4047
    return code;
×
4048
  }
4049
  *pOutput = (SNode*)pLimitNode;
11,293✔
4050
  return TSDB_CODE_SUCCESS;
11,293✔
4051
}
4052

4053
static bool rewriteTailOptNeedGroupSort(SIndefRowsFuncLogicNode* pIndef) {
11,293✔
4054
  if (1 != LIST_LENGTH(pIndef->node.pChildren)) {
11,293!
4055
    return false;
×
4056
  }
4057
  SNode* pChild = nodesListGetNode(pIndef->node.pChildren, 0);
11,293✔
4058
  return QUERY_NODE_LOGIC_PLAN_PARTITION == nodeType(pChild) ||
22,234✔
4059
         (QUERY_NODE_LOGIC_PLAN_SCAN == nodeType(pChild) && NULL != ((SScanLogicNode*)pChild)->pGroupTags);
10,941✔
4060
}
4061

4062
static int32_t rewriteTailOptCreateSort(SIndefRowsFuncLogicNode* pIndef, SLogicNode** pOutput) {
11,293✔
4063
  SSortLogicNode* pSort = NULL;
11,293✔
4064
  int32_t         code = nodesMakeNode(QUERY_NODE_LOGIC_PLAN_SORT, (SNode**)&pSort);
11,293✔
4065
  if (NULL == pSort) {
11,293!
4066
    return code;
×
4067
  }
4068

4069
  pSort->groupSort = rewriteTailOptNeedGroupSort(pIndef);
11,293✔
4070
  TSWAP(pSort->node.pChildren, pIndef->node.pChildren);
11,293✔
4071
  optResetParent((SLogicNode*)pSort);
11,293✔
4072
  pSort->node.precision = pIndef->node.precision;
11,293✔
4073

4074
  SFunctionNode* pTail = NULL;
11,293✔
4075
  SNode*         pFunc = NULL;
11,293✔
4076
  FOREACH(pFunc, pIndef->pFuncs) {
11,606!
4077
    if (FUNCTION_TYPE_TAIL == ((SFunctionNode*)pFunc)->funcType) {
11,606✔
4078
      pTail = (SFunctionNode*)pFunc;
11,293✔
4079
      break;
11,293✔
4080
    }
4081
  }
4082

4083
  // tail(expr, [limit, offset,] _rowts)
4084
  int32_t rowtsIndex = LIST_LENGTH(pTail->pParameterList) - 1;
11,293!
4085

4086
  SNode* pNewNode = NULL;
11,293✔
4087
  code = rewriteTailOptCreateOrderByExpr(nodesListGetNode(pTail->pParameterList, rowtsIndex), &pNewNode);
11,293✔
4088
  if (TSDB_CODE_SUCCESS == code) {
11,293!
4089
    code = nodesListMakeStrictAppend(&pSort->pSortKeys, pNewNode);
11,293✔
4090
  }
4091
  if (TSDB_CODE_SUCCESS == code) {
11,293!
4092
    code = nodesCloneList(((SLogicNode*)nodesListGetNode(pSort->node.pChildren, 0))->pTargets, &pSort->node.pTargets);
11,293✔
4093
  }
4094

4095
  if (TSDB_CODE_SUCCESS == code) {
11,293!
4096
    *pOutput = (SLogicNode*)pSort;
11,293✔
4097
  } else {
4098
    nodesDestroyNode((SNode*)pSort);
×
4099
  }
4100

4101
  return code;
11,293✔
4102
}
4103

4104
static int32_t rewriteTailOptCreateProjectExpr(SFunctionNode* pFunc, SNode** ppNode) {
11,630✔
4105
  SNode*  pExpr = NULL;
11,630✔
4106
  int32_t code = nodesCloneNode(nodesListGetNode(pFunc->pParameterList, 0), &pExpr);
11,630✔
4107
  if (NULL == pExpr) {
11,630!
4108
    return code;
×
4109
  }
4110
  tstrncpy(((SExprNode*)pExpr)->aliasName, pFunc->node.aliasName, TSDB_COL_NAME_LEN);
11,630✔
4111
  *ppNode = pExpr;
11,630✔
4112
  return code;
11,630✔
4113
}
4114

4115
static int32_t rewriteTailOptCreateProject(SIndefRowsFuncLogicNode* pIndef, SLogicNode** pOutput) {
11,293✔
4116
  SProjectLogicNode* pProject = NULL;
11,293✔
4117
  int32_t            code = nodesMakeNode(QUERY_NODE_LOGIC_PLAN_PROJECT, (SNode**)&pProject);
11,293✔
4118
  if (NULL == pProject) {
11,293!
4119
    return TSDB_CODE_OUT_OF_MEMORY;
×
4120
  }
4121

4122
  TSWAP(pProject->node.pTargets, pIndef->node.pTargets);
11,293✔
4123
  pProject->node.precision = pIndef->node.precision;
11,293✔
4124

4125
  SFunctionNode* pTail = NULL;
11,293✔
4126
  SNode*         pFunc = NULL;
11,293✔
4127
  FOREACH(pFunc, pIndef->pFuncs) {
22,923!
4128
    SNode* pNew = NULL;
11,630✔
4129
    code = rewriteTailOptCreateProjectExpr((SFunctionNode*)pFunc, &pNew);
11,630✔
4130
    if (TSDB_CODE_SUCCESS == code) {
11,630!
4131
      code = nodesListMakeStrictAppend(&pProject->pProjections, pNew);
11,630✔
4132
    }
4133
    if (TSDB_CODE_SUCCESS != code) {
11,630!
4134
      break;
×
4135
    }
4136
    if (FUNCTION_TYPE_TAIL == ((SFunctionNode*)pFunc)->funcType) {
11,630✔
4137
      pTail = (SFunctionNode*)pFunc;
11,293✔
4138
    }
4139
  }
4140

4141
  // tail(expr, [limit, offset,] _rowts)
4142
  int32_t limitIndex = LIST_LENGTH(pTail->pParameterList) > 2 ? 1 : -1;
11,293!
4143
  int32_t offsetIndex = LIST_LENGTH(pTail->pParameterList) > 3 ? 2 : -1;
11,293!
4144
  if (TSDB_CODE_SUCCESS == code) {
11,293!
4145
    code = rewriteTailOptCreateLimit(limitIndex < 0 ? NULL : nodesListGetNode(pTail->pParameterList, limitIndex),
11,293!
4146
                                     offsetIndex < 0 ? NULL : nodesListGetNode(pTail->pParameterList, offsetIndex),
6,144✔
4147
                                     &pProject->node.pLimit);
11,293✔
4148
  }
4149
  if (TSDB_CODE_SUCCESS == code) {
11,293!
4150
    *pOutput = (SLogicNode*)pProject;
11,293✔
4151
  } else {
4152
    nodesDestroyNode((SNode*)pProject);
×
4153
  }
4154
  return code;
11,293✔
4155
}
4156

4157
static int32_t rewriteTailOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan,
11,293✔
4158
                                       SIndefRowsFuncLogicNode* pIndef) {
4159
  SLogicNode* pSort = NULL;
11,293✔
4160
  SLogicNode* pProject = NULL;
11,293✔
4161
  int32_t     code = rewriteTailOptCreateSort(pIndef, &pSort);
11,293✔
4162
  if (TSDB_CODE_SUCCESS == code) {
11,293!
4163
    code = rewriteTailOptCreateProject(pIndef, &pProject);
11,293✔
4164
  }
4165
  if (TSDB_CODE_SUCCESS == code) {
11,293!
4166
    code = nodesListMakeAppend(&pProject->pChildren, (SNode*)pSort);
11,293✔
4167
    pSort->pParent = pProject;
11,293✔
4168
    pSort = NULL;
11,293✔
4169
  }
4170
  if (TSDB_CODE_SUCCESS == code) {
11,293!
4171
    code = replaceLogicNode(pLogicSubplan, (SLogicNode*)pIndef, pProject);
11,293✔
4172
  }
4173
  if (TSDB_CODE_SUCCESS == code) {
11,293!
4174
    nodesDestroyNode((SNode*)pIndef);
11,293✔
4175
  } else {
4176
    nodesDestroyNode((SNode*)pSort);
×
4177
    nodesDestroyNode((SNode*)pProject);
×
4178
  }
4179
  pCxt->optimized = true;
11,293✔
4180
  return code;
11,293✔
4181
}
4182

4183
static int32_t rewriteTailOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
1,472,149✔
4184
  SIndefRowsFuncLogicNode* pIndef =
4185
      (SIndefRowsFuncLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, rewriteTailOptMayBeOptimized, NULL);
1,472,149✔
4186

4187
  if (NULL == pIndef) {
1,472,244✔
4188
    return TSDB_CODE_SUCCESS;
1,460,951✔
4189
  }
4190

4191
  return rewriteTailOptimizeImpl(pCxt, pLogicSubplan, pIndef);
11,293✔
4192
}
4193

4194
static bool eliminateSetOpMayBeOptimized(SLogicNode* pNode, void* pCtx) {
3,207,345✔
4195
  SLogicNode* pParent = pNode->pParent;
3,207,345✔
4196
  if (NULL == pParent ||
3,207,345✔
4197
      QUERY_NODE_LOGIC_PLAN_AGG != nodeType(pParent) && QUERY_NODE_LOGIC_PLAN_PROJECT != nodeType(pParent) ||
2,258,084✔
4198
      LIST_LENGTH(pParent->pChildren) < 2) {
1,474,262!
4199
    return false;
3,040,162✔
4200
  }
4201
  if (nodeType(pNode) != nodeType(pNode->pParent) || LIST_LENGTH(pNode->pChildren) < 2) {
167,183✔
4202
    return false;
166,723✔
4203
  }
4204
  return true;
460✔
4205
}
4206

4207
static int32_t eliminateSetOpOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan,
460✔
4208
                                          SLogicNode* pSetOpNode) {
4209
  SNode* pSibling;
4210
  FOREACH(pSibling, pSetOpNode->pParent->pChildren) {
461!
4211
    if (nodesEqualNode(pSibling, (SNode*)pSetOpNode)) {
461✔
4212
      SNode* pChild;
4213
      FOREACH(pChild, pSetOpNode->pChildren) { ((SLogicNode*)pChild)->pParent = pSetOpNode->pParent; }
1,380!
4214
      INSERT_LIST(pSetOpNode->pParent->pChildren, pSetOpNode->pChildren);
460✔
4215

4216
      pSetOpNode->pChildren = NULL;
460✔
4217
      ERASE_NODE(pSetOpNode->pParent->pChildren);
460✔
4218
      pCxt->optimized = true;
460✔
4219
      return TSDB_CODE_SUCCESS;
460✔
4220
    }
4221
  }
4222

4223
  return TSDB_CODE_PLAN_INTERNAL_ERROR;
×
4224
}
4225

4226
static int32_t eliminateSetOpOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
949,288✔
4227
  SLogicNode* pSetOpNode = optFindPossibleNode(pLogicSubplan->pNode, eliminateSetOpMayBeOptimized, NULL);
949,288✔
4228
  if (NULL == pSetOpNode) {
949,352✔
4229
    return TSDB_CODE_SUCCESS;
948,894✔
4230
  }
4231

4232
  return eliminateSetOpOptimizeImpl(pCxt, pLogicSubplan, pSetOpNode);
458✔
4233
}
4234

4235
static bool rewriteUniqueOptMayBeOptimized(SLogicNode* pNode, void* pCtx) {
5,169,366✔
4236
  return QUERY_NODE_LOGIC_PLAN_INDEF_ROWS_FUNC == nodeType(pNode) && ((SIndefRowsFuncLogicNode*)pNode)->isUniqueFunc;
5,169,366✔
4237
}
4238

4239
static int32_t rewriteUniqueOptCreateGroupingSet(SNode* pExpr, SNode** ppNode) {
10,849✔
4240
  SGroupingSetNode* pGroupingSet = NULL;
10,849✔
4241
  int32_t           code = nodesMakeNode(QUERY_NODE_GROUPING_SET, (SNode**)&pGroupingSet);
10,849✔
4242
  if (NULL == pGroupingSet) {
10,849!
4243
    return code;
×
4244
  }
4245
  pGroupingSet->groupingSetType = GP_TYPE_NORMAL;
10,849✔
4246
  SExprNode* pGroupExpr = NULL;
10,849✔
4247
  code = nodesCloneNode(pExpr, (SNode**)&pGroupExpr);
10,849✔
4248
  if (TSDB_CODE_SUCCESS == code) {
10,849!
4249
    code = nodesListMakeStrictAppend(&pGroupingSet->pParameterList, (SNode*)pGroupExpr);
10,849✔
4250
  }
4251
  if (TSDB_CODE_SUCCESS != code) {
10,849!
4252
    nodesDestroyNode((SNode*)pGroupingSet);
×
4253
    return code;
×
4254
  }
4255
  *ppNode = (SNode*)pGroupingSet;
10,849✔
4256
  return code;
10,849✔
4257
}
4258

4259
static int32_t rewriteUniqueOptCreateFirstFunc(SFunctionNode* pSelectValue, SNode* pCol, SNode** ppNode) {
2,094✔
4260
  SFunctionNode* pFunc = NULL;
2,094✔
4261
  int32_t        code = nodesMakeNode(QUERY_NODE_FUNCTION, (SNode**)&pFunc);
2,094✔
4262
  if (NULL == pFunc) {
2,094!
4263
    return code;
×
4264
  }
4265

4266
  tstrncpy(pFunc->functionName, "first", TSDB_FUNC_NAME_LEN);
2,094✔
4267
  if (NULL != pSelectValue) {
2,094✔
4268
    tstrncpy(pFunc->node.aliasName, pSelectValue->node.aliasName, TSDB_COL_NAME_LEN);
1,798✔
4269
  } else {
4270
    int64_t pointer = (int64_t)pFunc;
296✔
4271
    char    name[TSDB_FUNC_NAME_LEN + TSDB_POINTER_PRINT_BYTES + TSDB_NAME_DELIMITER_LEN + 1] = {0};
296✔
4272
    int32_t len = tsnprintf(name, sizeof(name) - 1, "%s.%" PRId64, pFunc->functionName, pointer);
296✔
4273
    (void)taosHashBinary(name, len);
4274
    tstrncpy(pFunc->node.aliasName, name, TSDB_COL_NAME_LEN);
296✔
4275
  }
4276
  SNode* pNew = NULL;
2,094✔
4277
  code = nodesCloneNode(pCol, &pNew);
2,094✔
4278
  if (TSDB_CODE_SUCCESS == code) {
2,094!
4279
    code = nodesListMakeStrictAppend(&pFunc->pParameterList, pNew);
2,094✔
4280
  }
4281
  if (TSDB_CODE_SUCCESS == code) {
2,094!
4282
    code = fmGetFuncInfo(pFunc, NULL, 0);
2,094✔
4283
  }
4284

4285
  if (TSDB_CODE_SUCCESS != code) {
2,094!
4286
    nodesDestroyNode((SNode*)pFunc);
×
4287
    return code;
×
4288
  }
4289
  *ppNode = (SNode*)pFunc;
2,094✔
4290
  return code;
2,094✔
4291
}
4292

4293
static int32_t rewriteUniqueOptCreateAgg(SIndefRowsFuncLogicNode* pIndef, SLogicNode** pOutput) {
10,849✔
4294
  SAggLogicNode* pAgg = NULL;
10,849✔
4295
  int32_t        code = nodesMakeNode(QUERY_NODE_LOGIC_PLAN_AGG, (SNode**)&pAgg);
10,849✔
4296
  if (NULL == pAgg) {
10,849!
4297
    return code;
×
4298
  }
4299

4300
  TSWAP(pAgg->node.pChildren, pIndef->node.pChildren);
10,849✔
4301
  optResetParent((SLogicNode*)pAgg);
10,849✔
4302
  pAgg->node.precision = pIndef->node.precision;
10,849✔
4303
  pAgg->node.requireDataOrder = DATA_ORDER_LEVEL_IN_BLOCK;  // first function requirement
10,849✔
4304
  pAgg->node.resultDataOrder = DATA_ORDER_LEVEL_NONE;
10,849✔
4305

4306
  bool   hasSelectPrimaryKey = false;
10,849✔
4307
  SNode* pPrimaryKey = NULL;
10,849✔
4308
  SNode* pNode = NULL;
10,849✔
4309
  FOREACH(pNode, pIndef->pFuncs) {
25,934!
4310
    SFunctionNode* pFunc = (SFunctionNode*)pNode;
15,085✔
4311
    SNode*         pExpr = nodesListGetNode(pFunc->pParameterList, 0);
15,085✔
4312
    if (FUNCTION_TYPE_UNIQUE == pFunc->funcType) {
15,085✔
4313
      pPrimaryKey = nodesListGetNode(pFunc->pParameterList, 1);
10,849✔
4314
      SNode* pNew = NULL;
10,849✔
4315
      code = rewriteUniqueOptCreateGroupingSet(pExpr, &pNew);
10,849✔
4316
      if (TSDB_CODE_SUCCESS == code) {
10,849!
4317
        code = nodesListMakeStrictAppend(&pAgg->pGroupKeys, pNew);
10,849✔
4318
      }
4319
    } else if (PRIMARYKEY_TIMESTAMP_COL_ID == ((SColumnNode*)pExpr)->colId) {  // _select_value(ts) => first(ts)
4,236✔
4320
      hasSelectPrimaryKey = true;
1,798✔
4321
      SNode* pNew = NULL;
1,798✔
4322
      code = rewriteUniqueOptCreateFirstFunc(pFunc, pExpr, &pNew);
1,798✔
4323
      if (TSDB_CODE_SUCCESS == code) {
1,798!
4324
        code = nodesListMakeStrictAppend(&pAgg->pAggFuncs, pNew);
1,798✔
4325
      }
4326
    } else {  // _select_value(other_col)
4327
      SNode* pNew = NULL;
2,438✔
4328
      code = nodesCloneNode(pNode, &pNew);
2,438✔
4329
      if (TSDB_CODE_SUCCESS == code) {
2,438!
4330
        code = nodesListMakeStrictAppend(&pAgg->pAggFuncs, pNew);
2,438✔
4331
      }
4332
    }
4333
    if (TSDB_CODE_SUCCESS != code) {
15,085!
4334
      break;
×
4335
    }
4336
  }
4337

4338
  if (TSDB_CODE_SUCCESS == code) {
10,849!
4339
    code = createColumnByRewriteExprs(pAgg->pGroupKeys, &pAgg->node.pTargets);
10,849✔
4340
  }
4341
  if (TSDB_CODE_SUCCESS == code && NULL != pAgg->pAggFuncs) {
10,849!
4342
    code = createColumnByRewriteExprs(pAgg->pAggFuncs, &pAgg->node.pTargets);
2,094✔
4343
  }
4344

4345
  if (TSDB_CODE_SUCCESS == code && !hasSelectPrimaryKey && NULL != pAgg->pAggFuncs) {
10,849!
4346
    SNode* pNew = NULL;
296✔
4347
    code = rewriteUniqueOptCreateFirstFunc(NULL, pPrimaryKey, &pNew);
296✔
4348
    if (TSDB_CODE_SUCCESS == code) {
296!
4349
      code = nodesListMakeStrictAppend(&pAgg->pAggFuncs, pNew);
296✔
4350
    }
4351
  }
4352

4353
  if (TSDB_CODE_SUCCESS == code) {
10,849!
4354
    *pOutput = (SLogicNode*)pAgg;
10,849✔
4355
  } else {
4356
    nodesDestroyNode((SNode*)pAgg);
×
4357
  }
4358
  return code;
10,849✔
4359
}
4360

4361
static int32_t rewriteUniqueOptCreateProjectCol(SFunctionNode* pFunc, SNode** ppNode) {
15,085✔
4362
  SColumnNode* pCol = NULL;
15,085✔
4363
  int32_t      code = nodesMakeNode(QUERY_NODE_COLUMN, (SNode**)&pCol);
15,085✔
4364
  if (NULL == pCol) {
15,085!
4365
    return code;
×
4366
  }
4367

4368
  pCol->node.resType = pFunc->node.resType;
15,085✔
4369
  if (FUNCTION_TYPE_UNIQUE == pFunc->funcType) {
15,085✔
4370
    SExprNode* pExpr = (SExprNode*)nodesListGetNode(pFunc->pParameterList, 0);
10,849✔
4371
    if (QUERY_NODE_COLUMN == nodeType(pExpr)) {
10,849✔
4372
      PLAN_ERR_RET(nodesCloneNode((SNode*)pExpr, (SNode**)&pCol));
10,838!
4373
    } else {
4374
      tstrncpy(pCol->colName, pExpr->aliasName, TSDB_COL_NAME_LEN);
11✔
4375
    }
4376
  } else {
4377
    tstrncpy(pCol->colName, pFunc->node.aliasName, TSDB_COL_NAME_LEN);
4,236✔
4378
  }
4379
  tstrncpy(pCol->node.aliasName, pFunc->node.aliasName, TSDB_COL_NAME_LEN);
15,085✔
4380
  *ppNode = (SNode*)pCol;
15,085✔
4381
  return code;
15,085✔
4382
}
4383

4384
static int32_t rewriteUniqueOptCreateProject(SIndefRowsFuncLogicNode* pIndef, SLogicNode** pOutput) {
10,849✔
4385
  SProjectLogicNode* pProject = NULL;
10,849✔
4386
  int32_t            code = nodesMakeNode(QUERY_NODE_LOGIC_PLAN_PROJECT, (SNode**)&pProject);
10,849✔
4387
  if (NULL == pProject) {
10,849!
4388
    return code;
×
4389
  }
4390

4391
  TSWAP(pProject->node.pTargets, pIndef->node.pTargets);
10,849✔
4392
  pProject->node.precision = pIndef->node.precision;
10,849✔
4393
  pProject->node.requireDataOrder = DATA_ORDER_LEVEL_NONE;
10,849✔
4394
  pProject->node.resultDataOrder = DATA_ORDER_LEVEL_NONE;
10,849✔
4395

4396
  SNode* pNode = NULL;
10,849✔
4397
  FOREACH(pNode, pIndef->pFuncs) {
25,934!
4398
    SNode* pNew = NULL;
15,085✔
4399
    code = rewriteUniqueOptCreateProjectCol((SFunctionNode*)pNode, &pNew);
15,085✔
4400
    if (TSDB_CODE_SUCCESS == code) {
15,085!
4401
      code = nodesListMakeStrictAppend(&pProject->pProjections, pNew);
15,085✔
4402
    }
4403
    if (TSDB_CODE_SUCCESS != code) {
15,085!
4404
      break;
×
4405
    }
4406
  }
4407

4408
  if (TSDB_CODE_SUCCESS == code) {
10,849!
4409
    *pOutput = (SLogicNode*)pProject;
10,849✔
4410
  } else {
4411
    nodesDestroyNode((SNode*)pProject);
×
4412
  }
4413
  return code;
10,849✔
4414
}
4415

4416
static int32_t rewriteUniqueOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan,
10,849✔
4417
                                         SIndefRowsFuncLogicNode* pIndef) {
4418
  SLogicNode* pAgg = NULL;
10,849✔
4419
  SLogicNode* pProject = NULL;
10,849✔
4420
  int32_t     code = rewriteUniqueOptCreateAgg(pIndef, &pAgg);
10,849✔
4421
  if (TSDB_CODE_SUCCESS == code) {
10,849!
4422
    code = rewriteUniqueOptCreateProject(pIndef, &pProject);
10,849✔
4423
  }
4424
  if (TSDB_CODE_SUCCESS == code) {
10,849!
4425
    code = nodesListMakeAppend(&pProject->pChildren, (SNode*)pAgg);
10,849✔
4426
  }
4427
  if (TSDB_CODE_SUCCESS == code) {
10,849!
4428
    pAgg->pParent = pProject;
10,849✔
4429
    pAgg = NULL;
10,849✔
4430
    code = replaceLogicNode(pLogicSubplan, (SLogicNode*)pIndef, pProject);
10,849✔
4431
  }
4432
  if (TSDB_CODE_SUCCESS == code) {
10,849!
4433
    code = adjustLogicNodeDataRequirement(
10,849✔
4434
        pProject, NULL == pProject->pParent ? DATA_ORDER_LEVEL_NONE : pProject->pParent->requireDataOrder);
10,849!
4435
    pProject = NULL;
10,849✔
4436
  }
4437
  if (TSDB_CODE_SUCCESS == code) {
10,849!
4438
    nodesDestroyNode((SNode*)pIndef);
10,849✔
4439
  } else {
4440
    nodesDestroyNode((SNode*)pAgg);
×
4441
    nodesDestroyNode((SNode*)pProject);
×
4442
  }
4443
  pCxt->optimized = true;
10,849✔
4444
  return code;
10,849✔
4445
}
4446

4447
static int32_t rewriteUniqueOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
1,460,832✔
4448
  SIndefRowsFuncLogicNode* pIndef =
4449
      (SIndefRowsFuncLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, rewriteUniqueOptMayBeOptimized, NULL);
1,460,832✔
4450

4451
  if (NULL == pIndef) {
1,460,960✔
4452
    return TSDB_CODE_SUCCESS;
1,450,111✔
4453
  }
4454

4455
  return rewriteUniqueOptimizeImpl(pCxt, pLogicSubplan, pIndef);
10,849✔
4456
}
4457

4458
typedef struct SLastRowScanOptLastParaCkCxt {
4459
  bool hasTag;
4460
  bool hasCol;
4461
} SLastRowScanOptLastParaCkCxt;
4462

4463
static EDealRes lastRowScanOptLastParaIsTagImpl(SNode* pNode, void* pContext) {
18,042✔
4464
  if (QUERY_NODE_COLUMN == nodeType(pNode)) {
18,042!
4465
    SLastRowScanOptLastParaCkCxt* pCxt = pContext;
18,042✔
4466
    if (COLUMN_TYPE_TAG == ((SColumnNode*)pNode)->colType || COLUMN_TYPE_TBNAME == ((SColumnNode*)pNode)->colType) {
18,042!
4467
      pCxt->hasTag = true;
18,042✔
4468
    } else {
4469
      pCxt->hasCol = true;
×
4470
    }
4471
    return DEAL_RES_END;
18,042✔
4472
  }
4473
  return DEAL_RES_CONTINUE;
×
4474
}
4475

4476
static bool lastRowScanOptLastParaIsTag(SNode* pExpr) {
18,042✔
4477
  SLastRowScanOptLastParaCkCxt cxt = {.hasTag = false, .hasCol = false};
18,042✔
4478
  nodesWalkExpr(pExpr, lastRowScanOptLastParaIsTagImpl, &cxt);
18,042✔
4479
  return cxt.hasTag && !cxt.hasCol;
18,042!
4480
}
4481

4482
static bool hasSuitableCache(int8_t cacheLastMode, bool hasLastRow, bool hasLast) {
100,543✔
4483
  switch (cacheLastMode) {
100,543!
4484
    case TSDB_CACHE_MODEL_NONE:
62,796✔
4485
      return false;
62,796✔
4486
    case TSDB_CACHE_MODEL_LAST_ROW:
2,705✔
4487
      return hasLastRow;
2,705✔
4488
    case TSDB_CACHE_MODEL_LAST_VALUE:
13,150✔
4489
      return hasLast;
13,150✔
4490
    case TSDB_CACHE_MODEL_BOTH:
21,898✔
4491
      return true;
21,898✔
4492
    default:
×
4493
      break;
×
4494
  }
4495
  return false;
×
4496
}
4497

4498
/// @brief check if we can apply last row scan optimization
4499
/// @param lastColNum how many distinct last col specified
4500
/// @param lastColId only used when lastColNum equals 1, the col id of the only one last col
4501
/// @param selectNonPKColNum num of normal cols
4502
/// @param selectNonPKColId only used when selectNonPKColNum equals 1, the col id of the only one select col
4503
static bool lastRowScanOptCheckColNum(int32_t lastColNum, col_id_t lastColId, int32_t selectNonPKColNum,
47,908✔
4504
                                      col_id_t selectNonPKColId) {
4505
  // multi select non pk col + last func: select c1, c2, last(c1)
4506
  if (selectNonPKColNum > 1 && lastColNum > 0) return false;
47,908!
4507

4508
  if (selectNonPKColNum == 1) {
47,908✔
4509
    // select last(c1), last(c2), c1 ...
4510
    // which is not possible currently
4511
    if (lastColNum > 1) return false;
3,698!
4512

4513
    // select last(c1), c2 ...
4514
    if (lastColNum == 1 && lastColId != selectNonPKColId) return false;
3,698✔
4515
  }
4516
  return true;
47,628✔
4517
}
4518

4519
static bool isNeedSplitCacheLastFunc(SFunctionNode* pFunc, SScanLogicNode* pScan) {
4,736✔
4520
  int32_t funcType = pFunc->funcType;
4,736✔
4521
  if ((FUNCTION_TYPE_LAST_ROW != funcType ||
4,736!
4522
       (FUNCTION_TYPE_LAST_ROW == funcType && TSDB_CACHE_MODEL_LAST_VALUE == pScan->cacheLastMode)) &&
4,736✔
4523
      (FUNCTION_TYPE_LAST != funcType ||
2,350!
4524
       (FUNCTION_TYPE_LAST == funcType &&
2,350✔
4525
        (TSDB_CACHE_MODEL_LAST_ROW == pScan->cacheLastMode ||
2,350✔
4526
         QUERY_NODE_OPERATOR == nodeType(nodesListGetNode(pFunc->pParameterList, 0)) ||
1,250✔
4527
         QUERY_NODE_VALUE == nodeType(nodesListGetNode(pFunc->pParameterList, 0)) ||
1,242✔
4528
         COLUMN_TYPE_COLUMN != ((SColumnNode*)nodesListGetNode(pFunc->pParameterList, 0))->colType))) &&
3,557!
4529
      FUNCTION_TYPE_SELECT_VALUE != funcType && FUNCTION_TYPE_GROUP_KEY != funcType) {
2,335!
4530
    return true;
2,335✔
4531
  }
4532
  return false;
2,401✔
4533
}
4534

4535
static bool lastRowScanOptCheckFuncList(SLogicNode* pNode, int8_t cacheLastModel, bool* hasOtherFunc) {
31,541✔
4536
  bool            hasNonPKSelectFunc = false;
31,541✔
4537
  SNode*          pFunc = NULL;
31,541✔
4538
  int32_t         lastColNum = 0, selectNonPKColNum = 0;
31,541✔
4539
  col_id_t        lastColId = -1, selectNonPKColId = -1;
31,541✔
4540
  SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(((SAggLogicNode*)pNode)->node.pChildren, 0);
31,541✔
4541
  uint32_t        needSplitFuncCount = 0;
31,541✔
4542
  FOREACH(pFunc, ((SAggLogicNode*)pNode)->pAggFuncs) {
323,189!
4543
    SFunctionNode* pAggFunc = (SFunctionNode*)pFunc;
291,928✔
4544
    SNode*         pParam = nodesListGetNode(pAggFunc->pParameterList, 0);
291,928✔
4545
    if (FUNCTION_TYPE_LAST == pAggFunc->funcType) {
291,928✔
4546
      if (QUERY_NODE_COLUMN == nodeType(pParam)) {
45,178✔
4547
        SColumnNode* pCol = (SColumnNode*)pParam;
44,906✔
4548
        if (pCol->colType != COLUMN_TYPE_COLUMN && TSDB_CACHE_MODEL_LAST_ROW != cacheLastModel) {
44,906✔
4549
          needSplitFuncCount++;
44✔
4550
          *hasOtherFunc = true;
44✔
4551
        }
4552
        if (lastColId != pCol->colId) {
44,906!
4553
          lastColId = pCol->colId;
44,906✔
4554
          lastColNum++;
44,906✔
4555
        }
4556
      } else if (QUERY_NODE_VALUE == nodeType(pParam) || QUERY_NODE_OPERATOR == nodeType(pParam)) {
272!
4557
        needSplitFuncCount++;
272✔
4558
        *hasOtherFunc = true;
272✔
4559
      }
4560
      if (!lastRowScanOptCheckColNum(lastColNum, lastColId, selectNonPKColNum, selectNonPKColId)) {
45,178!
4561
        return false;
×
4562
      }
4563
      if (TSDB_CACHE_MODEL_LAST_ROW == cacheLastModel) {
45,178✔
4564
        needSplitFuncCount++;
1,244✔
4565
        *hasOtherFunc = true;
1,244✔
4566
      }
4567
    } else if (FUNCTION_TYPE_SELECT_VALUE == pAggFunc->funcType) {
246,750✔
4568
      if (QUERY_NODE_COLUMN == nodeType(pParam)) {
8,212!
4569
        SColumnNode* pCol = (SColumnNode*)pParam;
8,212✔
4570
        if (COLUMN_TYPE_COLUMN == pCol->colType && PRIMARYKEY_TIMESTAMP_COL_ID != pCol->colId) {
8,212✔
4571
          if (selectNonPKColId != pCol->colId) {
2,730!
4572
            selectNonPKColId = pCol->colId;
2,730✔
4573
            selectNonPKColNum++;
2,730✔
4574
          }
4575
        } else {
4576
          continue;
5,482✔
4577
        }
4578
      } else if (lastColNum > 0) {
×
4579
        return false;
×
4580
      }
4581
      if (!lastRowScanOptCheckColNum(lastColNum, lastColId, selectNonPKColNum, selectNonPKColId)) return false;
2,730✔
4582
    } else if (FUNCTION_TYPE_GROUP_KEY == pAggFunc->funcType) {
238,538✔
4583
      if (!lastRowScanOptLastParaIsTag(nodesListGetNode(pAggFunc->pParameterList, 0))) {
18,042!
4584
        return false;
×
4585
      }
4586
    } else if (FUNCTION_TYPE_LAST_ROW != pAggFunc->funcType) {
220,496✔
4587
      *hasOtherFunc = true;
177,147✔
4588
      needSplitFuncCount++;
177,147✔
4589
    } else if (FUNCTION_TYPE_LAST_ROW == pAggFunc->funcType && TSDB_CACHE_MODEL_LAST_VALUE == cacheLastModel) {
43,349!
4590
      *hasOtherFunc = true;
7,288✔
4591
      needSplitFuncCount++;
7,288✔
4592
    }
4593
  }
4594
  if (needSplitFuncCount >= ((SAggLogicNode*)pNode)->pAggFuncs->length) {
31,261✔
4595
    return false;
216✔
4596
  }
4597

4598
  return true;
31,045✔
4599
}
4600

4601
static bool lastRowScanOptCheckLastCache(SAggLogicNode* pAgg, SScanLogicNode* pScan) {
1,580,189✔
4602
  if ((pAgg->hasLastRow == pAgg->hasLast && !pAgg->hasLastRow) || (!pAgg->hasLast && !pAgg->hasLastRow) ||
1,580,189!
4603
      NULL != pAgg->pGroupKeys || NULL != pScan->node.pConditions ||
113,162✔
4604
      !hasSuitableCache(pScan->cacheLastMode, pAgg->hasLastRow, pAgg->hasLast) ||
100,551✔
4605
      IS_TSWINDOW_SPECIFIED(pScan->scanRange)) {
31,673✔
4606
    return false;
1,548,646✔
4607
  }
4608

4609
  return true;
31,541✔
4610
}
4611

4612
static bool lastRowScanOptMayBeOptimized(SLogicNode* pNode, void* pCtx) {
5,113,232✔
4613
  if (QUERY_NODE_LOGIC_PLAN_AGG != nodeType(pNode) || 1 != LIST_LENGTH(pNode->pChildren) ||
5,113,232!
4614
      QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(nodesListGetNode(pNode->pChildren, 0))) {
1,041,040✔
4615
    return false;
4,323,411✔
4616
  }
4617

4618
  SAggLogicNode*  pAgg = (SAggLogicNode*)pNode;
789,823✔
4619
  SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(pNode->pChildren, 0);
789,823✔
4620
  if (!lastRowScanOptCheckLastCache(pAgg, pScan)) {
789,854✔
4621
    return false;
774,375✔
4622
  }
4623

4624
  bool hasOtherFunc = false;
15,479✔
4625
  if (!lastRowScanOptCheckFuncList(pNode, pScan->cacheLastMode, &hasOtherFunc)) {
15,479✔
4626
    return false;
248✔
4627
  }
4628

4629
  if (hasOtherFunc) {
15,231✔
4630
    return false;
8,810✔
4631
  }
4632

4633
  return true;
6,421✔
4634
}
4635

4636
typedef struct SLastRowScanOptSetColDataTypeCxt {
4637
  bool       doAgg;
4638
  SNodeList* pLastCols;
4639
  SNodeList* pOtherCols;
4640
  int32_t    funcType;
4641
  int32_t    pkBytes;
4642
  int32_t    code;
4643
} SLastRowScanOptSetColDataTypeCxt;
4644

4645
static EDealRes lastRowScanOptGetColAndSetDataType(SNode* pNode, void* pContext, bool setType) {
12,203✔
4646
  if (QUERY_NODE_COLUMN == nodeType(pNode)) {
12,203✔
4647
    SLastRowScanOptSetColDataTypeCxt* pCxt = pContext;
12,125✔
4648
    if (pCxt->doAgg) {
12,125✔
4649
      pCxt->code = nodesListMakeAppend(&pCxt->pLastCols, pNode);
12,094✔
4650
      if (TSDB_CODE_SUCCESS != pCxt->code) {
12,094!
4651
        return DEAL_RES_ERROR;
×
4652
      }
4653
      if (setType) getLastCacheDataType(&(((SColumnNode*)pNode)->node.resType), pCxt->pkBytes);
12,094!
4654
    } else {
4655
      SNode* pCol = NULL;
31✔
4656
      FOREACH(pCol, pCxt->pLastCols) {
74!
4657
        if (nodesEqualNode(pCol, pNode)) {
43!
4658
          if (setType) getLastCacheDataType(&(((SColumnNode*)pNode)->node.resType), pCxt->pkBytes);
×
4659
          break;
×
4660
        }
4661
      }
4662
    }
4663
    return DEAL_RES_IGNORE_CHILD;
12,125✔
4664
  }
4665
  return DEAL_RES_CONTINUE;
78✔
4666
}
4667

4668
static EDealRes lastRowScanOptGetLastCols(SNode* pNode, void* pContext) {
12,094✔
4669
  return lastRowScanOptGetColAndSetDataType(pNode, pContext, false);
12,094✔
4670
}
4671

4672
static EDealRes lastRowScanOptSetColDataType(SNode* pNode, void* pContext) {
109✔
4673
  return lastRowScanOptGetColAndSetDataType(pNode, pContext, true);
109✔
4674
}
4675

4676
static void lastRowScanOptSetLastTargets(SNodeList* pTargets, SNodeList* pLastCols, SNodeList* pLastRowCols, bool erase,
6,986✔
4677
                                         int32_t pkBytes) {
4678
  SNode* pTarget = NULL;
6,986✔
4679
  WHERE_EACH(pTarget, pTargets) {
41,489!
4680
    bool   found = false;
34,503✔
4681
    SNode* pCol = NULL;
34,503✔
4682
    FOREACH(pCol, pLastCols) {
365,842!
4683
      if (nodesEqualNode(pCol, pTarget)) {
355,527✔
4684
        getLastCacheDataType(&(((SColumnNode*)pTarget)->node.resType), pkBytes);
24,188✔
4685
        found = true;
24,188✔
4686
        break;
24,188✔
4687
      }
4688
    }
4689
    if (!found && nodeListNodeEqual(pLastRowCols, pTarget)) {
34,503✔
4690
      found = true;
4,876✔
4691
    }
4692

4693
    if (!found && erase) {
34,503✔
4694
      ERASE_NODE(pTargets);
2,665✔
4695
      continue;
2,665✔
4696
    }
4697
    WHERE_NEXT;
31,838✔
4698
  }
4699
}
6,986✔
4700

4701
static void lastRowScanOptRemoveUslessTargets(SNodeList* pTargets, SNodeList* pList1, SNodeList* pList2,
3,493✔
4702
                                              SNodeList* pList3) {
4703
  SNode* pTarget = NULL;
3,493✔
4704
  WHERE_EACH(pTarget, pTargets) {
20,799!
4705
    bool   found = false;
17,306✔
4706
    SNode* pCol = NULL;
17,306✔
4707
    FOREACH(pCol, pList1) {
183,036!
4708
      if (nodesEqualNode(pCol, pTarget)) {
177,824✔
4709
        found = true;
12,094✔
4710
        break;
12,094✔
4711
      }
4712
    }
4713
    if (!found) {
17,306✔
4714
      FOREACH(pCol, pList2) {
7,246✔
4715
        if (nodesEqualNode(pCol, pTarget)) {
3,802✔
4716
          found = true;
1,768✔
4717
          break;
1,768✔
4718
        }
4719
      }
4720
    }
4721

4722
    if (!found && nodeListNodeEqual(pList3, pTarget)) {
17,306✔
4723
      found = true;
2,428✔
4724
    }
4725

4726
    if (!found) {
17,306✔
4727
      ERASE_NODE(pTargets);
1,016✔
4728
      continue;
1,016✔
4729
    }
4730
    WHERE_NEXT;
16,290✔
4731
  }
4732
}
3,493✔
4733

4734
static int32_t lastRowScanBuildFuncTypes(SScanLogicNode* pScan, SColumnNode* pColNode, int32_t funcType) {
4,914✔
4735
  SFunctParam* pFuncTypeParam = taosMemoryCalloc(1, sizeof(SFunctParam));
4,914!
4736
  if (NULL == pFuncTypeParam) {
4,914!
4737
    return terrno;
×
4738
  }
4739
  pFuncTypeParam->type = funcType;
4,914✔
4740
  if (NULL == pScan->pFuncTypes) {
4,914✔
4741
    pScan->pFuncTypes = taosArrayInit(pScan->pScanCols->length, sizeof(SFunctParam));
186✔
4742
    if (NULL == pScan->pFuncTypes) {
186!
4743
      taosMemoryFree(pFuncTypeParam);
×
4744
      return terrno;
×
4745
    }
4746
  }
4747

4748
  pFuncTypeParam->pCol = taosMemoryCalloc(1, sizeof(SColumn));
4,914!
4749
  if (NULL == pFuncTypeParam->pCol) {
4,914!
4750
    taosMemoryFree(pFuncTypeParam);
×
4751
    return terrno;
×
4752
  }
4753
  pFuncTypeParam->pCol->colId = pColNode->colId;
4,914✔
4754
  tstrncpy(pFuncTypeParam->pCol->name, pColNode->colName, TSDB_COL_NAME_LEN);
4,914✔
4755
  if (NULL == taosArrayPush(pScan->pFuncTypes, pFuncTypeParam)) {
9,828!
4756
    taosMemoryFree(pFuncTypeParam);
×
4757
    return terrno;
×
4758
  }
4759

4760
  taosMemoryFree(pFuncTypeParam);
4,914!
4761
  return TSDB_CODE_SUCCESS;
4,914✔
4762
}
4763

4764
static int32_t lastRowScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
1,449,690✔
4765
  SAggLogicNode* pAgg = (SAggLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, lastRowScanOptMayBeOptimized, NULL);
1,449,690✔
4766

4767
  if (NULL == pAgg) {
1,449,763✔
4768
    return TSDB_CODE_SUCCESS;
1,443,342✔
4769
  }
4770

4771
  SLastRowScanOptSetColDataTypeCxt cxt = {.doAgg = true, .pLastCols = NULL, .pOtherCols = NULL};
6,421✔
4772
  SNode*                           pNode = NULL;
6,421✔
4773
  SColumnNode*                     pPKTsCol = NULL;
6,421✔
4774
  SColumnNode*                     pNonPKCol = NULL;
6,421✔
4775
  SScanLogicNode*                  pScan = (SScanLogicNode*)nodesListGetNode(pAgg->node.pChildren, 0);
6,421✔
4776
  pScan->scanType = SCAN_TYPE_LAST_ROW;
6,421✔
4777
  pScan->igLastNull = pAgg->hasLast ? true : false;
6,421✔
4778
  SArray*    isDuplicateCol = taosArrayInit(pScan->pScanCols->length, sizeof(bool));
6,421✔
4779
  SNodeList* pLastRowCols = NULL;
6,421✔
4780
  bool       adjLastRowTsColName = false;
6,421✔
4781
  char       tsColName[TSDB_COL_NAME_LEN] = {0};
6,421✔
4782
  int32_t    code = 0;
6,421✔
4783

4784
  FOREACH(pNode, pAgg->pAggFuncs) {
34,171!
4785
    SFunctionNode* pFunc = (SFunctionNode*)pNode;
27,750✔
4786
    int32_t        funcType = pFunc->funcType;
27,750✔
4787
    SNode*         pParamNode = NULL;
27,750✔
4788
    if (FUNCTION_TYPE_LAST == funcType) {
27,750✔
4789
      (void)nodesListErase(pFunc->pParameterList, nodesListGetCell(pFunc->pParameterList, 1));
12,094✔
4790
      nodesWalkExpr(nodesListGetNode(pFunc->pParameterList, 0), lastRowScanOptGetLastCols, &cxt);
12,094✔
4791
      if (TSDB_CODE_SUCCESS != cxt.code) break;
12,094!
4792
    }
4793
    FOREACH(pParamNode, pFunc->pParameterList) {
70,416!
4794
      if (FUNCTION_TYPE_LAST_ROW == funcType || FUNCTION_TYPE_LAST == funcType) {
81,237✔
4795
        int32_t len = tsnprintf(pFunc->functionName, sizeof(pFunc->functionName),
38,571✔
4796
                                FUNCTION_TYPE_LAST_ROW == funcType ? "_cache_last_row" : "_cache_last");
4797
        pFunc->functionName[len] = '\0';
38,571✔
4798
        code = fmGetFuncInfo(pFunc, NULL, 0);
38,571✔
4799
        if (TSDB_CODE_SUCCESS != code) {
38,571!
4800
          nodesClearList(cxt.pLastCols);
×
4801
          break;
×
4802
        }
4803
        cxt.funcType = pFunc->funcType;
38,571✔
4804
        cxt.pkBytes = (pFunc->hasPk) ? pFunc->pkBytes : 0;
38,571✔
4805
        // add duplicate cols which be removed for both last_row, last
4806
        if (pAgg->hasLast && pAgg->hasLastRow) {
38,571✔
4807
          if (QUERY_NODE_COLUMN == nodeType(pParamNode)) {
7,110✔
4808
            SNode* pColNode = NULL;
7,102✔
4809
            int    i = 0;
7,102✔
4810
            FOREACH(pColNode, pScan->pScanCols) {
236,344!
4811
              bool  isDup = false;
229,242✔
4812
              bool* isDuplicate = taosArrayGet(isDuplicateCol, i);
229,242✔
4813
              if (NULL == isDuplicate) {
229,242✔
4814
                if (NULL == taosArrayInsert(isDuplicateCol, i, &isDup)) {
2,403!
4815
                  code = terrno;
×
4816
                  break;
×
4817
                }
4818
                isDuplicate = taosArrayGet(isDuplicateCol, i);
2,403✔
4819
              }
4820
              i++;
229,242✔
4821
              if (nodesEqualNode(pParamNode, pColNode)) {
229,242✔
4822
                if (*isDuplicate) {
11,790✔
4823
                  if (0 == strncmp(((SColumnNode*)pColNode)->colName, "#dup_col.", 9)) {
9,398✔
4824
                    continue;
6,971✔
4825
                  }
4826
                  SNode* newColNode = NULL;
4,710✔
4827
                  code = nodesCloneNode(pColNode, &newColNode);
4,710✔
4828
                  if (TSDB_CODE_SUCCESS != code) {
4,710!
4829
                    break;
×
4830
                  }
4831
                  snprintf(((SColumnNode*)newColNode)->colName, TSDB_COL_NAME_LEN, "#dup_col.%p", newColNode);
4,710✔
4832
                  snprintf(((SColumnNode*)pParamNode)->colName, TSDB_COL_NAME_LEN, "#dup_col.%p", newColNode);
4,710✔
4833
                  if (FUNCTION_TYPE_LAST_ROW == funcType &&
4,710✔
4834
                      ((SColumnNode*)pParamNode)->colId == PRIMARYKEY_TIMESTAMP_COL_ID) {
4,503✔
4835
                    if (!adjLastRowTsColName) {
2,469✔
4836
                      adjLastRowTsColName = true;
186✔
4837
                      tstrncpy(tsColName, ((SColumnNode*)pParamNode)->colName, TSDB_COL_NAME_LEN);
186✔
4838
                    } else {
4839
                      tstrncpy(((SColumnNode*)pParamNode)->colName, tsColName, TSDB_COL_NAME_LEN);
2,283✔
4840
                      nodesDestroyNode(newColNode);
2,283✔
4841
                      continue;
2,283✔
4842
                    }
4843
                  }
4844

4845
                  code = nodesListStrictAppend(pScan->pScanCols, newColNode);
2,427✔
4846
                  if (TSDB_CODE_SUCCESS != code) break;
2,427!
4847
                  isDup = true;
2,427✔
4848
                  if (NULL == taosArrayInsert(isDuplicateCol, pScan->pScanCols->length, &isDup)) {
2,427!
4849
                    code = terrno;
×
4850
                    break;
×
4851
                  }
4852
                  SNode* pNew = NULL;
2,427✔
4853
                  code = nodesCloneNode(newColNode, &pNew);
2,427✔
4854
                  if (TSDB_CODE_SUCCESS != code) break;
2,427!
4855
                  code = nodesListStrictAppend(pScan->node.pTargets, pNew);
2,427✔
4856
                  if (TSDB_CODE_SUCCESS != code) break;
2,427!
4857
                  if (funcType != FUNCTION_TYPE_LAST) {
2,427✔
4858
                    pNew = NULL;
2,220✔
4859
                    code = nodesCloneNode(newColNode, &pNew);
2,220✔
4860
                    if (TSDB_CODE_SUCCESS != code) break;
2,220!
4861
                    code = nodesListMakeAppend(&pLastRowCols, pNew);
2,220✔
4862
                    if (TSDB_CODE_SUCCESS != code) break;
2,220!
4863
                  }
4864

4865
                  code = lastRowScanBuildFuncTypes(pScan, (SColumnNode*)newColNode, pFunc->funcType);
2,427✔
4866
                  if (TSDB_CODE_SUCCESS != code) break;
2,427!
4867
                } else {
4868
                  isDup = true;
2,392✔
4869
                  *isDuplicate = isDup;
2,392✔
4870
                  if (funcType != FUNCTION_TYPE_LAST && !nodeListNodeEqual(cxt.pLastCols, pColNode)) {
2,392!
4871
                    SNode* pNew = NULL;
207✔
4872
                    code = nodesCloneNode(pColNode, &pNew);
207✔
4873
                    if (TSDB_CODE_SUCCESS != code) break;
207!
4874
                    code = nodesListMakeStrictAppend(&pLastRowCols, pNew);
207✔
4875
                    if (TSDB_CODE_SUCCESS != code) break;
207!
4876
                  }
4877
                  code = lastRowScanBuildFuncTypes(pScan, (SColumnNode*)pColNode, pFunc->funcType);
2,392✔
4878
                  if (TSDB_CODE_SUCCESS != code) break;
2,392!
4879
                }
4880
                continue;
4,819✔
4881
              } else if (nodeListNodeEqual(pFunc->pParameterList, pColNode)) {
217,452✔
4882
                if (funcType != FUNCTION_TYPE_LAST && ((SColumnNode*)pColNode)->colId == PRIMARYKEY_TIMESTAMP_COL_ID &&
4,400✔
4883
                    !nodeListNodeEqual(pLastRowCols, pColNode)) {
2,185✔
4884
                  SNode* pNew = NULL;
95✔
4885
                  code = nodesCloneNode(pColNode, &pNew);
95✔
4886
                  if (TSDB_CODE_SUCCESS != code) break;
95!
4887
                  code = nodesListMakeAppend(&pLastRowCols, pNew);
95✔
4888
                  if (TSDB_CODE_SUCCESS != code) break;
95!
4889

4890
                  code = lastRowScanBuildFuncTypes(pScan, (SColumnNode*)pColNode, pFunc->funcType);
95✔
4891
                  if (TSDB_CODE_SUCCESS != code) break;
95!
4892
                  isDup = true;
95✔
4893
                  *isDuplicate = isDup;
95✔
4894
                }
4895
              }
4896
            }
4897
            if (TSDB_CODE_SUCCESS != code) break;
7,102!
4898
            ;
4899
            FOREACH(pColNode, pScan->pScanPseudoCols) {
7,102!
4900
              if (nodesEqualNode(pParamNode, pColNode)) {
×
4901
                if (funcType != FUNCTION_TYPE_LAST) {
×
4902
                  SNode* pNew = NULL;
×
4903
                  code = nodesCloneNode(pColNode, &pNew);
×
4904
                  if (TSDB_CODE_SUCCESS != code) break;
×
4905
                  code = nodesListMakeAppend(&pLastRowCols, pNew);
×
4906
                  if (TSDB_CODE_SUCCESS != code) break;
×
4907
                }
4908
              }
4909
            }
4910
          }
4911
        }
4912

4913
        if (TSDB_CODE_SUCCESS != code) break;
38,571!
4914
        if (pFunc->hasPk) {
38,571✔
4915
          code = nodesListMakeAppend(&cxt.pOtherCols,
7,993✔
4916
                                     nodesListGetNode(pFunc->pParameterList, LIST_LENGTH(pFunc->pParameterList) - 1));
7,993!
4917
        }
4918
        if (TSDB_CODE_SUCCESS != code) break;
38,571!
4919
      } else {
4920
        pNode = nodesListGetNode(pFunc->pParameterList, 0);
4,095✔
4921
        code = nodesListMakeAppend(&cxt.pOtherCols, pNode);
4,095✔
4922
        if (TSDB_CODE_SUCCESS != code) break;
4,095!
4923

4924
        if (FUNCTION_TYPE_SELECT_VALUE == funcType) {
4,095✔
4925
          if (nodeType(pNode) == QUERY_NODE_COLUMN) {
3,944!
4926
            SColumnNode* pCol = (SColumnNode*)pNode;
3,944✔
4927
            if (pCol->colId == PRIMARYKEY_TIMESTAMP_COL_ID) {
3,944✔
4928
              pPKTsCol = pCol;
2,661✔
4929
            } else {
4930
              pNonPKCol = pCol;
1,283✔
4931
            }
4932
          }
4933
        }
4934
      }
4935
    }
4936
    if (TSDB_CODE_SUCCESS != code) break;
27,750!
4937
  }
4938

4939
  if (TSDB_CODE_SUCCESS != code) {
6,421!
4940
    taosArrayDestroy(isDuplicateCol);
×
4941
    nodesClearList(cxt.pLastCols);
×
4942
    return code;
×
4943
  }
4944

4945
  if (NULL != cxt.pLastCols) {
6,421✔
4946
    cxt.doAgg = false;
3,493✔
4947
    cxt.funcType = FUNCTION_TYPE_CACHE_LAST;
3,493✔
4948

4949
    lastRowScanOptSetLastTargets(pScan->pScanCols, cxt.pLastCols, pLastRowCols, true, cxt.pkBytes);
3,493✔
4950
    nodesWalkExprs(pScan->pScanPseudoCols, lastRowScanOptSetColDataType, &cxt);
3,493✔
4951
    if (TSDB_CODE_SUCCESS != cxt.code) {
3,493!
4952
      nodesClearList(cxt.pLastCols);
×
4953
      nodesClearList(cxt.pOtherCols);
×
4954
      taosArrayDestroy(isDuplicateCol);
×
4955
      return cxt.code;
×
4956
    }
4957
    lastRowScanOptSetLastTargets(pScan->node.pTargets, cxt.pLastCols, pLastRowCols, false, cxt.pkBytes);
3,493✔
4958
    lastRowScanOptRemoveUslessTargets(pScan->node.pTargets, cxt.pLastCols, cxt.pOtherCols, pLastRowCols);
3,493✔
4959
    if (pPKTsCol &&
3,493✔
4960
        ((cxt.pLastCols->length == 1 && nodesEqualNode((SNode*)pPKTsCol, nodesListGetNode(cxt.pLastCols, 0))) ||
1,710!
4961
         (pScan->node.pTargets->length == 2 && cxt.pkBytes > 0))) {
1,624✔
4962
      // when select last(ts),tbname,ts from ..., we add another ts to targets
4963
      snprintf(pPKTsCol->colName, TSDB_COL_NAME_LEN, "#sel_val.%p", pPKTsCol);
1,549✔
4964
      SNode* pNew = NULL;
1,549✔
4965
      code = nodesCloneNode((SNode*)pPKTsCol, &pNew);
1,549✔
4966
      if (TSDB_CODE_SUCCESS == code) {
1,549!
4967
        code = nodesListAppend(pScan->node.pTargets, pNew);
1,549✔
4968
      }
4969
      if (TSDB_CODE_SUCCESS != code) {
1,549!
4970
        nodesClearList(cxt.pLastCols);
×
4971
        nodesClearList(cxt.pOtherCols);
×
4972
        taosArrayDestroy(isDuplicateCol);
×
4973
        return code;
×
4974
      }
4975
    }
4976

4977
    if (pNonPKCol && cxt.pLastCols->length == 1 &&
4,243!
4978
        nodesEqualNode((SNode*)pNonPKCol, nodesListGetNode(cxt.pLastCols, 0))) {
750✔
4979
      // when select last(c1), c1 from ..., we add c1 to targets
4980
      snprintf(pNonPKCol->colName, TSDB_COL_NAME_LEN, "#sel_val.%p", pNonPKCol);
723✔
4981
      SNode* pNew = NULL;
723✔
4982
      code = nodesCloneNode((SNode*)pNonPKCol, &pNew);
723✔
4983
      if (TSDB_CODE_SUCCESS == code) {
723!
4984
        code = nodesListAppend(pScan->node.pTargets, pNew);
723✔
4985
      }
4986
    }
4987

4988
    nodesClearList(cxt.pLastCols);
3,493✔
4989
  }
4990
  nodesClearList(cxt.pOtherCols);
6,421✔
4991

4992
  pAgg->hasLastRow = false;
6,421✔
4993
  pAgg->hasLast = false;
6,421✔
4994

4995
  pCxt->optimized = true;
6,421✔
4996
  taosArrayDestroy(isDuplicateCol);
6,421✔
4997
  return TSDB_CODE_SUCCESS;
6,335✔
4998
}
4999

5000
static bool splitCacheLastFuncOptMayBeOptimized(SLogicNode* pNode, void* pCtx) {
5,121,041✔
5001
  if (QUERY_NODE_LOGIC_PLAN_AGG != nodeType(pNode) || 1 != LIST_LENGTH(pNode->pChildren) ||
5,121,041!
5002
      QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(nodesListGetNode(pNode->pChildren, 0))) {
1,041,621✔
5003
    return false;
4,330,646✔
5004
  }
5005

5006
  SAggLogicNode*  pAgg = (SAggLogicNode*)pNode;
790,405✔
5007
  SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(pNode->pChildren, 0);
790,405✔
5008
  if (!lastRowScanOptCheckLastCache(pAgg, pScan)) {
790,440✔
5009
    return false;
774,327✔
5010
  }
5011

5012
  bool hasOtherFunc = false;
16,062✔
5013
  if (!lastRowScanOptCheckFuncList(pNode, pScan->cacheLastMode, &hasOtherFunc)) {
16,062✔
5014
    return false;
248✔
5015
  }
5016

5017
  if (pAgg->hasGroup || !hasOtherFunc) {
15,814✔
5018
    return false;
15,471✔
5019
  }
5020

5021
  return true;
343✔
5022
}
5023

5024
static int32_t splitCacheLastFuncOptCreateAggLogicNode(SAggLogicNode** pNewAgg, SAggLogicNode* pAgg, SNodeList* pFunc,
343✔
5025
                                                       SNodeList* pTargets) {
5026
  SAggLogicNode* pNew = NULL;
343✔
5027
  int32_t        code = nodesMakeNode(QUERY_NODE_LOGIC_PLAN_AGG, (SNode**)&pNew);
343✔
5028
  if (NULL == pNew) {
343!
5029
    nodesDestroyList(pFunc);
×
5030
    nodesDestroyList(pTargets);
×
5031
    return code;
×
5032
  }
5033

5034
  pNew->hasLastRow = false;
343✔
5035
  pNew->hasLast = false;
343✔
5036
  SNode* pFuncNode = NULL;
343✔
5037
  FOREACH(pFuncNode, pFunc) {
2,678!
5038
    SFunctionNode* pFunc = (SFunctionNode*)pFuncNode;
2,335✔
5039
    if (FUNCTION_TYPE_LAST_ROW == pFunc->funcType) {
2,335✔
5040
      pNew->hasLastRow = true;
1,052✔
5041
    } else if (FUNCTION_TYPE_LAST == pFunc->funcType) {
1,283✔
5042
      pNew->hasLast = true;
1,128✔
5043
    }
5044
  }
5045

5046
  pNew->hasTimeLineFunc = pAgg->hasTimeLineFunc;
343✔
5047
  pNew->hasGroupKeyOptimized = false;
343✔
5048
  pNew->onlyHasKeepOrderFunc = pAgg->onlyHasKeepOrderFunc;
343✔
5049
  pNew->node.groupAction = pAgg->node.groupAction;
343✔
5050
  pNew->node.requireDataOrder = pAgg->node.requireDataOrder;
343✔
5051
  pNew->node.resultDataOrder = pAgg->node.resultDataOrder;
343✔
5052
  pNew->node.pTargets = pTargets;
343✔
5053
  pNew->pAggFuncs = pFunc;
343✔
5054
  code = nodesCloneList(pAgg->pGroupKeys, &pNew->pGroupKeys);
343✔
5055
  if (TSDB_CODE_SUCCESS != code) {
343!
5056
    nodesDestroyNode((SNode*)pNew);
×
5057
    return code;
×
5058
  }
5059
  code = nodesCloneNode(pAgg->node.pConditions, &pNew->node.pConditions);
343✔
5060
  if (TSDB_CODE_SUCCESS != code) {
343!
5061
    nodesDestroyNode((SNode*)pNew);
×
5062
    return code;
×
5063
  }
5064
  pNew->isGroupTb = pAgg->isGroupTb;
343✔
5065
  pNew->isPartTb = pAgg->isPartTb;
343✔
5066
  pNew->hasGroup = pAgg->hasGroup;
343✔
5067
  code = nodesCloneList(pAgg->node.pChildren, &pNew->node.pChildren);
343✔
5068
  if (TSDB_CODE_SUCCESS != code) {
343!
5069
    nodesDestroyNode((SNode*)pNew);
×
5070
    return code;
×
5071
  }
5072

5073
  SNode* pNode = nodesListGetNode(pNew->node.pChildren, 0);
343✔
5074
  if (QUERY_NODE_LOGIC_PLAN_SCAN == nodeType(pNode)) {
343!
5075
    SScanLogicNode* pScan = (SScanLogicNode*)pNode;
343✔
5076
    SNodeList*      pOldScanCols = NULL;
343✔
5077
    TSWAP(pScan->pScanCols, pOldScanCols);
343✔
5078
    nodesDestroyList(pScan->pScanPseudoCols);
343✔
5079
    pScan->pScanPseudoCols = NULL;
343✔
5080
    nodesDestroyList(pScan->node.pTargets);
343✔
5081
    pScan->node.pTargets = NULL;
343✔
5082
    SNodeListNode* list = NULL;
343✔
5083
    code = nodesMakeNode(QUERY_NODE_NODE_LIST, (SNode**)&list);
343✔
5084
    if (!list) {
343!
5085
      nodesDestroyNode((SNode*)pNew);
×
5086
      return code;
×
5087
    }
5088
    list->pNodeList = pFunc;
343✔
5089
    code = nodesCollectColumnsFromNode((SNode*)list, NULL, COLLECT_COL_TYPE_COL, &pScan->pScanCols);
343✔
5090
    if (TSDB_CODE_SUCCESS != code) {
343!
5091
      nodesDestroyNode((SNode*)pNew);
×
5092
      return code;
×
5093
    }
5094
    code = nodesCollectColumnsFromNode((SNode*)list, NULL, COLLECT_COL_TYPE_TAG, &pScan->pScanPseudoCols);
343✔
5095
    if (TSDB_CODE_SUCCESS != code) {
343!
5096
      nodesDestroyNode((SNode*)pNew);
×
5097
      return code;
×
5098
    }
5099
    nodesFree(list);
343✔
5100
    bool found = false;
343✔
5101
    FOREACH(pNode, pScan->pScanCols) {
414✔
5102
      if (PRIMARYKEY_TIMESTAMP_COL_ID == ((SColumnNode*)pNode)->colId) {
393✔
5103
        found = true;
322✔
5104
        break;
322✔
5105
      }
5106
    }
5107
    if (!found) {
343✔
5108
      FOREACH(pNode, pOldScanCols) {
42!
5109
        if (PRIMARYKEY_TIMESTAMP_COL_ID == ((SColumnNode*)pNode)->colId) {
42✔
5110
          SNode* pTmp = NULL;
21✔
5111
          code = nodesCloneNode(pNode, &pTmp);
21✔
5112
          if (TSDB_CODE_SUCCESS == code) {
21!
5113
            code = nodesListMakeStrictAppend(&pScan->pScanCols, pTmp);
21✔
5114
          }
5115
          break;
21✔
5116
        }
5117
      }
5118
      if (TSDB_CODE_SUCCESS != code) {
21!
5119
        nodesDestroyNode((SNode*)pNew);
×
5120
        return code;
×
5121
      }
5122
    }
5123
    nodesDestroyList(pOldScanCols);
343✔
5124
    code = createColumnByRewriteExprs(pScan->pScanCols, &pScan->node.pTargets);
343✔
5125
    if (TSDB_CODE_SUCCESS != code) {
343!
5126
      nodesDestroyNode((SNode*)pNew);
×
5127
      return code;
×
5128
    }
5129
    code = createColumnByRewriteExprs(pScan->pScanPseudoCols, &pScan->node.pTargets);
343✔
5130
    if (TSDB_CODE_SUCCESS != code) {
343!
5131
      nodesDestroyNode((SNode*)pNew);
×
5132
      return code;
×
5133
    }
5134
    OPTIMIZE_FLAG_CLEAR_MASK(pScan->node.optimizedFlag, OPTIMIZE_FLAG_SCAN_PATH);
343✔
5135
  }
5136

5137
  *pNewAgg = pNew;
343✔
5138

5139
  return TSDB_CODE_SUCCESS;
343✔
5140
}
5141

5142
static int32_t splitCacheLastFuncOptModifyAggLogicNode(SAggLogicNode* pAgg) {
343✔
5143
  pAgg->hasTimeLineFunc = false;
343✔
5144
  pAgg->onlyHasKeepOrderFunc = true;
343✔
5145

5146
  return TSDB_CODE_SUCCESS;
343✔
5147
}
5148

5149
static int32_t splitCacheLastFuncOptCreateMergeLogicNode(SMergeLogicNode** pNew, SAggLogicNode* pAgg1,
343✔
5150
                                                         SAggLogicNode* pAgg2) {
5151
  SMergeLogicNode* pMerge = NULL;
343✔
5152
  int32_t          code = nodesMakeNode(QUERY_NODE_LOGIC_PLAN_MERGE, (SNode**)&pMerge);
343✔
5153
  if (NULL == pMerge) {
343!
5154
    return code;
×
5155
  }
5156
  pMerge->colsMerge = true;
343✔
5157
  pMerge->numOfChannels = 2;
343✔
5158
  pMerge->srcGroupId = -1;
343✔
5159
  pMerge->node.precision = pAgg1->node.precision;
343✔
5160

5161
  SNode* pNewAgg1 = NULL;
343✔
5162
  code = nodesCloneNode((SNode*)pAgg1, &pNewAgg1);
343✔
5163
  if (TSDB_CODE_SUCCESS != code) {
343!
5164
    nodesDestroyNode((SNode*)pMerge);
×
5165
    return code;
×
5166
  }
5167
  SNode* pNewAgg2 = NULL;
343✔
5168
  code = nodesCloneNode((SNode*)pAgg2, &pNewAgg2);
343✔
5169
  if (TSDB_CODE_SUCCESS != code) {
343!
5170
    nodesDestroyNode(pNewAgg1);
×
5171
    nodesDestroyNode((SNode*)pMerge);
×
5172
    return code;
×
5173
  }
5174

5175
  ((SAggLogicNode*)pNewAgg1)->node.pParent = (SLogicNode*)pMerge;
343✔
5176
  ((SAggLogicNode*)pNewAgg2)->node.pParent = (SLogicNode*)pMerge;
343✔
5177

5178
  SNode* pNode = NULL;
343✔
5179
  FOREACH(pNode, ((SAggLogicNode*)pNewAgg1)->node.pChildren) { ((SLogicNode*)pNode)->pParent = (SLogicNode*)pNewAgg1; }
686!
5180
  FOREACH(pNode, ((SAggLogicNode*)pNewAgg2)->node.pChildren) { ((SLogicNode*)pNode)->pParent = (SLogicNode*)pNewAgg2; }
686!
5181

5182
  SNodeList* pNewTargets1 = NULL;
343✔
5183
  code = nodesCloneList(pAgg1->node.pTargets, &pNewTargets1);
343✔
5184
  if (TSDB_CODE_SUCCESS == code) {
343!
5185
    code = nodesListMakeStrictAppendList(&pMerge->node.pTargets, pNewTargets1);
343✔
5186
  }
5187
  SNodeList* pNewTargets2 = NULL;
343✔
5188
  if (TSDB_CODE_SUCCESS == code) {
343!
5189
    code = nodesCloneList(pAgg2->node.pTargets, &pNewTargets2);
343✔
5190
  }
5191
  if (TSDB_CODE_SUCCESS == code) {
343!
5192
    code = nodesListMakeStrictAppendList(&pMerge->node.pTargets, pNewTargets2);
343✔
5193
  }
5194
  if (TSDB_CODE_SUCCESS == code) {
343!
5195
    code = nodesListMakeStrictAppend(&pMerge->node.pChildren, pNewAgg1);
343✔
5196
  }
5197
  if (TSDB_CODE_SUCCESS == code) {
343!
5198
    code = nodesListMakeStrictAppend(&pMerge->node.pChildren, pNewAgg2);
343✔
5199
  }
5200

5201
  if (TSDB_CODE_SUCCESS != code) {
343!
5202
    nodesDestroyNode(pNewAgg1);
×
5203
    nodesDestroyNode(pNewAgg2);
×
5204
    nodesDestroyNode((SNode*)pMerge);
×
5205
  } else {
5206
    *pNew = pMerge;
343✔
5207
  }
5208

5209
  return code;
343✔
5210
}
5211

5212
static int32_t splitCacheLastFuncOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
1,450,051✔
5213
  SAggLogicNode* pAgg =
5214
      (SAggLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, splitCacheLastFuncOptMayBeOptimized, NULL);
1,450,051✔
5215

5216
  if (NULL == pAgg) {
1,450,099✔
5217
    return TSDB_CODE_SUCCESS;
1,449,757✔
5218
  }
5219
  SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(pAgg->node.pChildren, 0);
342✔
5220
  SNode*          pNode = NULL;
343✔
5221
  SNodeList*      pAggFuncList = NULL;
343✔
5222
  int32_t         code = 0;
343✔
5223

5224
  {
5225
    bool hasLast = false;
343✔
5226
    bool hasLastRow = false;
343✔
5227
    WHERE_EACH(pNode, pAgg->pAggFuncs) {
5,079!
5228
      SFunctionNode* pFunc = (SFunctionNode*)pNode;
4,736✔
5229
      int32_t        funcType = pFunc->funcType;
4,736✔
5230

5231
      if (isNeedSplitCacheLastFunc(pFunc, pScan)) {
4,736✔
5232
        SNode* pNew = NULL;
2,335✔
5233
        code = nodesCloneNode(pNode, &pNew);
2,335✔
5234
        if (TSDB_CODE_SUCCESS == code) {
2,335!
5235
          code = nodesListMakeStrictAppend(&pAggFuncList, pNew);
2,335✔
5236
        }
5237
        if (TSDB_CODE_SUCCESS != code) {
2,335!
5238
          break;
×
5239
        }
5240
        ERASE_NODE(pAgg->pAggFuncs);
2,335✔
5241
        continue;
2,335✔
5242
      }
5243
      if (FUNCTION_TYPE_LAST_ROW == funcType) {
2,401✔
5244
        hasLastRow = true;
1,179✔
5245
      } else if (FUNCTION_TYPE_LAST == funcType) {
1,222!
5246
        hasLast = true;
1,222✔
5247
      }
5248
      WHERE_NEXT;
2,401✔
5249
    }
5250
    pAgg->hasLast = hasLast;
343✔
5251
    pAgg->hasLastRow = hasLastRow;
343✔
5252
  }
5253
  if (TSDB_CODE_SUCCESS != code) {
343!
5254
    nodesDestroyList(pAggFuncList);
×
5255
    return code;
×
5256
  }
5257

5258
  if (NULL == pAggFuncList) {
343!
5259
    planError("empty agg func list while splite projections, funcNum:%d", pAgg->pAggFuncs->length);
×
5260
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
×
5261
  }
5262

5263
  SNodeList* pTargets = NULL;
343✔
5264
  {
5265
    WHERE_EACH(pNode, pAgg->node.pTargets) {
5,079!
5266
      SColumnNode* pCol = (SColumnNode*)pNode;
4,736✔
5267
      SNode*       pFuncNode = NULL;
4,736✔
5268
      bool         found = false;
4,736✔
5269
      FOREACH(pFuncNode, pAggFuncList) {
70,253!
5270
        SFunctionNode* pFunc = (SFunctionNode*)pFuncNode;
67,852✔
5271
        if (0 == strcmp(pFunc->node.aliasName, pCol->colName)) {
67,852✔
5272
          SNode* pNew = NULL;
2,335✔
5273
          code = nodesCloneNode(pNode, &pNew);
2,335✔
5274
          if (TSDB_CODE_SUCCESS == code) {
2,335!
5275
            code = nodesListMakeStrictAppend(&pTargets, pNew);
2,335✔
5276
          }
5277
          found = true;
2,335✔
5278
          break;
2,335✔
5279
        }
5280
      }
5281
      if (TSDB_CODE_SUCCESS != code) {
4,736!
5282
        break;
×
5283
      }
5284
      if (found) {
4,736✔
5285
        ERASE_NODE(pAgg->node.pTargets);
2,335✔
5286
        continue;
2,335✔
5287
      }
5288
      WHERE_NEXT;
2,401✔
5289
    }
5290
  }
5291
  if (TSDB_CODE_SUCCESS != code) {
343!
5292
    nodesDestroyList(pTargets);
×
5293
    return code;
×
5294
  }
5295

5296
  if (NULL == pTargets) {
343!
5297
    planError("empty target func list while splite projections, targetsNum:%d", pAgg->node.pTargets->length);
×
5298
    nodesDestroyList(pAggFuncList);
×
5299
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
×
5300
  }
5301

5302
  SMergeLogicNode* pMerge = NULL;
343✔
5303
  SAggLogicNode*   pNewAgg = NULL;
343✔
5304
  code = splitCacheLastFuncOptCreateAggLogicNode(&pNewAgg, pAgg, pAggFuncList, pTargets);
343✔
5305
  if (TSDB_CODE_SUCCESS == code) {
343!
5306
    code = splitCacheLastFuncOptModifyAggLogicNode(pAgg);
343✔
5307
  }
5308
  if (TSDB_CODE_SUCCESS == code) {
343!
5309
    code = splitCacheLastFuncOptCreateMergeLogicNode(&pMerge, pNewAgg, pAgg);
343✔
5310
  }
5311
  if (TSDB_CODE_SUCCESS == code) {
343!
5312
    code = replaceLogicNode(pLogicSubplan, (SLogicNode*)pAgg, (SLogicNode*)pMerge);
343✔
5313
  }
5314

5315
  nodesDestroyNode((SNode*)pAgg);
343✔
5316
  nodesDestroyNode((SNode*)pNewAgg);
343✔
5317

5318
  if (TSDB_CODE_SUCCESS != code) {
343!
5319
    nodesDestroyNode((SNode*)pMerge);
×
5320
  }
5321

5322
  pCxt->optimized = true;
343✔
5323
  return code;
343✔
5324
}
5325

5326
// merge projects
5327
static bool mergeProjectsMayBeOptimized(SLogicNode* pNode, void* pCtx) {
5,496,818✔
5328
  if (QUERY_NODE_LOGIC_PLAN_PROJECT != nodeType(pNode) || 1 != LIST_LENGTH(pNode->pChildren)) {
5,496,818✔
5329
    return false;
3,969,901✔
5330
  }
5331
  SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pNode->pChildren, 0);
1,526,917✔
5332
  if (QUERY_NODE_LOGIC_PLAN_PROJECT != nodeType(pChild) || 1 < LIST_LENGTH(pChild->pChildren) ||
1,526,936✔
5333
      NULL != pChild->pConditions || NULL != pChild->pLimit || NULL != pChild->pSlimit) {
168,645!
5334
    return false;
1,386,256✔
5335
  }
5336

5337
  return true;
140,680✔
5338
}
5339

5340
typedef struct SMergeProjectionsContext {
5341
  SProjectLogicNode* pChildProj;
5342
  int32_t            errCode;
5343
} SMergeProjectionsContext;
5344

5345
static EDealRes mergeProjectionsExpr2(SNode** pNode, void* pContext) {
636,122✔
5346
  SMergeProjectionsContext* pCxt = pContext;
636,122✔
5347
  SProjectLogicNode*        pChildProj = pCxt->pChildProj;
636,122✔
5348
  if (QUERY_NODE_COLUMN == nodeType(*pNode)) {
636,122✔
5349
    SColumnNode* pProjCol = (SColumnNode*)(*pNode);
387,462✔
5350
    SNode*       pProjection;
5351
    int32_t      projIdx = 1;
387,462✔
5352
    FOREACH(pProjection, pChildProj->pProjections) {
9,257,067!
5353
      if (isColRefExpr(pProjCol, (SExprNode*)pProjection)) {
9,257,067✔
5354
        SNode* pExpr = NULL;
387,462✔
5355
        pCxt->errCode = nodesCloneNode(pProjection, &pExpr);
387,462✔
5356
        if (pExpr == NULL) {
387,462!
5357
          return DEAL_RES_ERROR;
×
5358
        }
5359
        snprintf(((SExprNode*)pExpr)->aliasName, sizeof(((SExprNode*)pExpr)->aliasName), "%s",
387,462✔
5360
                 ((SExprNode*)*pNode)->aliasName);
387,462✔
5361
        nodesDestroyNode(*pNode);
387,462✔
5362
        *pNode = pExpr;
387,462✔
5363
        return DEAL_RES_IGNORE_CHILD;
387,462✔
5364
      }
5365
    }
5366
  }
5367
  return DEAL_RES_CONTINUE;
248,660✔
5368
}
5369

5370
static EDealRes mergeProjectionsExpr(SNode** pNode, void* pContext) {
×
5371
  SMergeProjectionsContext* pCxt = pContext;
×
5372
  SProjectLogicNode*        pChildProj = pCxt->pChildProj;
×
5373
  if (QUERY_NODE_COLUMN == nodeType(*pNode)) {
×
5374
    SNode* pTarget;
5375
    FOREACH(pTarget, ((SLogicNode*)pChildProj)->pTargets) {
×
5376
      if (nodesEqualNode(pTarget, *pNode)) {
×
5377
        SNode* pProjection;
5378
        FOREACH(pProjection, pChildProj->pProjections) {
×
5379
          if (0 == strcmp(((SColumnNode*)pTarget)->colName, ((SExprNode*)pProjection)->aliasName)) {
×
5380
            SNode* pExpr = NULL;
×
5381
            pCxt->errCode = nodesCloneNode(pProjection, &pExpr);
×
5382
            if (pExpr == NULL) {
×
5383
              return DEAL_RES_ERROR;
×
5384
            }
5385
            snprintf(((SExprNode*)pExpr)->aliasName, sizeof(((SExprNode*)pExpr)->aliasName), "%s",
×
5386
                     ((SExprNode*)*pNode)->aliasName);
×
5387
            nodesDestroyNode(*pNode);
×
5388
            *pNode = pExpr;
×
5389
            return DEAL_RES_IGNORE_CHILD;
×
5390
          }
5391
        }
5392
      }
5393
    }
5394
  }
5395
  return DEAL_RES_CONTINUE;
×
5396
}
5397

5398
static int32_t mergeProjectsOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan, SLogicNode* pSelfNode) {
140,680✔
5399
  SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pSelfNode->pChildren, 0);
140,680✔
5400
  if (((SProjectLogicNode*)pChild)->ignoreGroupId) {
140,680✔
5401
    ((SProjectLogicNode*)pSelfNode)->inputIgnoreGroup = true;
123,251✔
5402
  }
5403
  SMergeProjectionsContext cxt = {.pChildProj = (SProjectLogicNode*)pChild, .errCode = TSDB_CODE_SUCCESS};
140,680✔
5404
  nodesRewriteExprs(((SProjectLogicNode*)pSelfNode)->pProjections, mergeProjectionsExpr2, &cxt);
140,680✔
5405
  int32_t code = cxt.errCode;
140,680✔
5406

5407
  if (TSDB_CODE_SUCCESS == code) {
140,680!
5408
    if (1 == LIST_LENGTH(pChild->pChildren)) {
278,636!
5409
      SLogicNode* pGrandChild = (SLogicNode*)nodesListGetNode(pChild->pChildren, 0);
137,956✔
5410
      code = replaceLogicNode(pLogicSubplan, pChild, pGrandChild);
137,956✔
5411
    } else {  // no grand child
5412
      NODES_CLEAR_LIST(pSelfNode->pChildren);
2,724✔
5413
    }
5414
  }
5415

5416
  if (TSDB_CODE_SUCCESS == code) {
140,680!
5417
    NODES_CLEAR_LIST(pChild->pChildren);
140,680✔
5418
  }
5419
  nodesDestroyNode((SNode*)pChild);
140,680✔
5420
  pCxt->optimized = true;
140,680✔
5421
  return code;
140,680✔
5422
}
5423

5424
static int32_t mergeProjectsOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
1,612,817✔
5425
  SLogicNode* pProjectNode = optFindPossibleNode(pLogicSubplan->pNode, mergeProjectsMayBeOptimized, NULL);
1,612,817✔
5426
  if (NULL == pProjectNode) {
1,612,888✔
5427
    return TSDB_CODE_SUCCESS;
1,472,248✔
5428
  }
5429

5430
  return mergeProjectsOptimizeImpl(pCxt, pLogicSubplan, pProjectNode);
140,640✔
5431
}
5432

5433
static bool tagScanOptShouldBeOptimized(SLogicNode* pNode, void* pCtx) {
5,095,509✔
5434
  if (QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(pNode) || (SCAN_TYPE_TAG == ((SScanLogicNode*)pNode)->scanType)) {
5,095,509✔
5435
    return false;
3,365,973✔
5436
  }
5437
  SScanLogicNode* pScan = (SScanLogicNode*)pNode;
1,729,536✔
5438
  if (pScan->hasNormalCols) {
1,729,536✔
5439
    return false;
1,508,819✔
5440
  }
5441
  if (pScan->tableType == TSDB_SYSTEM_TABLE) {
220,717✔
5442
    return false;
16✔
5443
  }
5444
  if (NULL == pNode->pParent || QUERY_NODE_LOGIC_PLAN_AGG != nodeType(pNode->pParent) ||
220,701✔
5445
      1 != LIST_LENGTH(pNode->pParent->pChildren)) {
105,840✔
5446
    return false;
114,864✔
5447
  }
5448

5449
  SAggLogicNode* pAgg = (SAggLogicNode*)(pNode->pParent);
105,837✔
5450
  if (NULL == pAgg->pGroupKeys || NULL != pAgg->pAggFuncs || keysHasCol(pAgg->pGroupKeys) ||
105,837!
5451
      !planOptNodeListHasTbname(pAgg->pGroupKeys)) {
13,189✔
5452
    return false;
105,501✔
5453
  }
5454

5455
  SNode* pGroupKey = NULL;
333✔
5456
  FOREACH(pGroupKey, pAgg->pGroupKeys) {
718!
5457
    SNode* pGroup = NULL;
388✔
5458
    FOREACH(pGroup, ((SGroupingSetNode*)pGroupKey)->pParameterList) {
773!
5459
      if (QUERY_NODE_COLUMN != nodeType(pGroup)) {
388✔
5460
        return false;
3✔
5461
      }
5462
    }
5463
  }
5464
  return true;
330✔
5465
}
5466

5467
static int32_t tagScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
1,443,299✔
5468
  SScanLogicNode* pScanNode =
5469
      (SScanLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, tagScanOptShouldBeOptimized, NULL);
1,443,299✔
5470
  if (NULL == pScanNode) {
1,443,330✔
5471
    return TSDB_CODE_SUCCESS;
1,443,008✔
5472
  }
5473

5474
  pScanNode->scanType = SCAN_TYPE_TAG;
322✔
5475
  SNode* pTarget = NULL;
322✔
5476
  FOREACH(pTarget, pScanNode->node.pTargets) {
322!
5477
    if (PRIMARYKEY_TIMESTAMP_COL_ID == ((SColumnNode*)(pTarget))->colId) {
330!
5478
      ERASE_NODE(pScanNode->node.pTargets);
330✔
5479
      break;
330✔
5480
    }
5481
  }
5482

5483
  NODES_DESTORY_LIST(pScanNode->pScanCols);
322✔
5484

5485
  SLogicNode* pAgg = pScanNode->node.pParent;
330✔
5486
  if (NULL == pAgg->pParent) {
330!
5487
    SNodeList* pScanTargets = NULL;
×
5488
    int32_t    code = nodesMakeList(&pScanTargets);
×
5489
    if (TSDB_CODE_SUCCESS != code) {
×
5490
      return code;
×
5491
    }
5492

5493
    SNode* pAggTarget = NULL;
×
5494
    FOREACH(pAggTarget, pAgg->pTargets) {
×
5495
      SNode* pScanTarget = NULL;
×
5496
      FOREACH(pScanTarget, pScanNode->node.pTargets) {
×
5497
        if (0 == strcmp(((SColumnNode*)pAggTarget)->colName, ((SColumnNode*)pScanTarget)->colName)) {
×
5498
          SNode* pNew = NULL;
×
5499
          code = nodesCloneNode(pScanTarget, &pNew);
×
5500
          if (TSDB_CODE_SUCCESS == code) {
×
5501
            code = nodesListAppend(pScanTargets, pNew);
×
5502
          }
5503
          break;
×
5504
        }
5505
      }
5506
      if (TSDB_CODE_SUCCESS != code) {
×
5507
        break;
×
5508
      }
5509
    }
5510
    nodesDestroyList(pScanNode->node.pTargets);
×
5511
    pScanNode->node.pTargets = pScanTargets;
×
5512
  }
5513

5514
  pScanNode->onlyMetaCtbIdx = false;
330✔
5515

5516
  pCxt->optimized = true;
330✔
5517
  return TSDB_CODE_SUCCESS;
330✔
5518
}
5519

5520
static bool pushDownLimitOptShouldBeOptimized(SLogicNode* pNode, void* pCtx) {
6,641,777✔
5521
  if ((NULL == pNode->pLimit && pNode->pSlimit == NULL) || 1 != LIST_LENGTH(pNode->pChildren)) {
6,641,777✔
5522
    return false;
6,325,880✔
5523
  }
5524

5525
  SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pNode->pChildren, 0);
315,897✔
5526
  if (pChild->pLimit || pChild->pSlimit) return false;
315,897✔
5527
  return true;
241,562✔
5528
}
5529

5530
static void swapLimit(SLogicNode* pParent, SLogicNode* pChild) {
42,395✔
5531
  pChild->pLimit = pParent->pLimit;
42,395✔
5532
  pParent->pLimit = NULL;
42,395✔
5533
}
42,395✔
5534

5535
static int32_t pushDownLimitHow(SLogicNode* pNodeWithLimit, SLogicNode* pNodeLimitPushTo, bool* pPushed);
5536
static int32_t pushDownLimitTo(SLogicNode* pNodeWithLimit, SLogicNode* pNodeLimitPushTo, bool* pPushed) {
231,420✔
5537
  int32_t code = 0;
231,420✔
5538
  bool    cloned;
5539
  switch (nodeType(pNodeLimitPushTo)) {
231,420✔
5540
    case QUERY_NODE_LOGIC_PLAN_WINDOW: {
12,259✔
5541
      SWindowLogicNode* pWindow = (SWindowLogicNode*)pNodeLimitPushTo;
12,259✔
5542
      if (pWindow->winType != WINDOW_TYPE_INTERVAL) break;
12,259✔
5543
      code = cloneLimit(pNodeWithLimit, pNodeLimitPushTo, CLONE_LIMIT_SLIMIT, &cloned);
6,346✔
5544
      if (TSDB_CODE_SUCCESS == code) {
6,346!
5545
        *pPushed = true;
6,346✔
5546
      }
5547
      return code;
6,346✔
5548
    }
5549
    case QUERY_NODE_LOGIC_PLAN_SORT:
33,468✔
5550
      if (((SSortLogicNode*)pNodeLimitPushTo)->calcGroupId) break;
33,468✔
5551
      // fall through
5552
    case QUERY_NODE_LOGIC_PLAN_FILL: {
5553
      SNode* pChild = NULL;
33,663✔
5554
      code = cloneLimit(pNodeWithLimit, pNodeLimitPushTo, CLONE_LIMIT_SLIMIT, &cloned);
33,663✔
5555
      if (TSDB_CODE_SUCCESS != code) {
33,663!
5556
        return code;
×
5557
      }
5558
      FOREACH(pChild, pNodeLimitPushTo->pChildren) {
67,326!
5559
        code = pushDownLimitHow(pNodeLimitPushTo, (SLogicNode*)pChild, &cloned);
33,663✔
5560
        if (TSDB_CODE_SUCCESS != code) {
33,663!
5561
          return code;
×
5562
        }
5563
      }
5564
      *pPushed = true;
33,663✔
5565
      return code;
33,663✔
5566
    }
5567
    case QUERY_NODE_LOGIC_PLAN_AGG: {
46,224✔
5568
      if (nodeType(pNodeWithLimit) == QUERY_NODE_LOGIC_PLAN_PROJECT &&
88,363✔
5569
          (isPartTagAgg((SAggLogicNode*)pNodeLimitPushTo) || isPartTableAgg((SAggLogicNode*)pNodeLimitPushTo))) {
77,788✔
5570
        // when part by tag/tbname, slimit will be cloned to agg, and it will be pipelined.
5571
        // The scan below will do scanning with group order
5572
        code = cloneLimit(pNodeWithLimit, pNodeLimitPushTo, CLONE_SLIMIT, &cloned);
7,677✔
5573
        if (TSDB_CODE_SUCCESS == code) {
7,677!
5574
          *pPushed = cloned;
7,677✔
5575
        }
5576
        return code;
7,677✔
5577
      }
5578
      // else if not part by tag and tbname, the partition node below indicates that results are sorted, the agg node
5579
      // can be pipelined.
5580
      if (nodeType(pNodeWithLimit) == QUERY_NODE_LOGIC_PLAN_PROJECT && LIST_LENGTH(pNodeLimitPushTo->pChildren) == 1) {
38,547!
5581
        SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pNodeLimitPushTo->pChildren, 0);
34,462✔
5582
        if (nodeType(pChild) == QUERY_NODE_LOGIC_PLAN_PARTITION) {
34,462✔
5583
          pNodeLimitPushTo->forceCreateNonBlockingOptr = true;
417✔
5584
          code = cloneLimit(pNodeWithLimit, pNodeLimitPushTo, CLONE_SLIMIT, &cloned);
417✔
5585
          if (TSDB_CODE_SUCCESS == code) {
417!
5586
            *pPushed = cloned;
417✔
5587
          }
5588
          return code;
417✔
5589
        }
5590
        // Currently, partColOpt is executed after pushDownLimitOpt, and partColOpt will replace partition node with
5591
        // sort node.
5592
        // To avoid dependencies between these two optimizations, we add sort node too.
5593
        if (nodeType(pChild) == QUERY_NODE_LOGIC_PLAN_SORT && ((SSortLogicNode*)pChild)->calcGroupId) {
34,045!
5594
          pNodeLimitPushTo->forceCreateNonBlockingOptr = true;
×
5595
          code = cloneLimit(pNodeWithLimit, pNodeLimitPushTo, CLONE_SLIMIT, &cloned);
×
5596
          if (TSDB_CODE_SUCCESS == code) {
×
5597
            *pPushed = cloned;
×
5598
          }
5599
          return code;
×
5600
        }
5601
      }
5602
      break;
38,130✔
5603
    }
5604
    case QUERY_NODE_LOGIC_PLAN_SCAN:
51,487✔
5605
      if (nodeType(pNodeWithLimit) == QUERY_NODE_LOGIC_PLAN_PROJECT && pNodeWithLimit->pLimit) {
51,487✔
5606
        if (((SProjectLogicNode*)pNodeWithLimit)->inputIgnoreGroup) {
43,009✔
5607
          code = cloneLimit(pNodeWithLimit, pNodeLimitPushTo, CLONE_LIMIT, &cloned);
614✔
5608
        } else {
5609
          swapLimit(pNodeWithLimit, pNodeLimitPushTo);
42,395✔
5610
        }
5611
        if (TSDB_CODE_SUCCESS == code) {
43,010!
5612
          *pPushed = true;
43,010✔
5613
        }
5614
        return code;
43,010✔
5615
      }
5616
      break;
8,478✔
5617
    case QUERY_NODE_LOGIC_PLAN_JOIN: {
12,940✔
5618
      code = cloneLimit(pNodeWithLimit, pNodeLimitPushTo, CLONE_LIMIT, &cloned);
12,940✔
5619
      break;
12,940✔
5620
    }
5621
    default:
74,751✔
5622
      break;
74,751✔
5623
  }
5624
  *pPushed = false;
140,308✔
5625
  return code;
140,308✔
5626
}
5627

5628
static int32_t pushDownLimitHow(SLogicNode* pNodeWithLimit, SLogicNode* pNodeLimitPushTo, bool* pPushed) {
275,224✔
5629
  switch (nodeType(pNodeWithLimit)) {
275,224✔
5630
    case QUERY_NODE_LOGIC_PLAN_PROJECT:
181,731✔
5631
    case QUERY_NODE_LOGIC_PLAN_FILL:
5632
      return pushDownLimitTo(pNodeWithLimit, pNodeLimitPushTo, pPushed);
181,731✔
5633
    case QUERY_NODE_LOGIC_PLAN_SORT: {
84,385✔
5634
      SSortLogicNode* pSort = (SSortLogicNode*)pNodeWithLimit;
84,385✔
5635
      if (sortPriKeyOptIsPriKeyOrderBy(pSort->pSortKeys))
84,385✔
5636
        return pushDownLimitTo(pNodeWithLimit, pNodeLimitPushTo, pPushed);
49,689✔
5637
    }
5638
    default:
5639
      break;
43,804✔
5640
  }
5641
  *pPushed = false;
43,804✔
5642
  return TSDB_CODE_SUCCESS;
43,804✔
5643
}
5644

5645
static int32_t pushDownLimitOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
1,840,266✔
5646
  SLogicNode* pNode = optFindPossibleNode(pLogicSubplan->pNode, pushDownLimitOptShouldBeOptimized, NULL);
1,840,266✔
5647
  if (NULL == pNode) {
1,840,377✔
5648
    return TSDB_CODE_SUCCESS;
1,598,817✔
5649
  }
5650

5651
  SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pNode->pChildren, 0);
241,560✔
5652
  nodesDestroyNode(pChild->pLimit);
241,562✔
5653
  bool    pushed = false;
241,561✔
5654
  int32_t code = pushDownLimitHow(pNode, pChild, &pushed);
241,561✔
5655
  if (TSDB_CODE_SUCCESS != code) {
241,562!
5656
    return code;
×
5657
  }
5658
  if (pushed) {
241,562✔
5659
    pCxt->optimized = true;
84,839✔
5660
  }
5661
  return TSDB_CODE_SUCCESS;
241,562✔
5662
}
5663

5664
typedef struct STbCntScanOptInfo {
5665
  SAggLogicNode*  pAgg;
5666
  SScanLogicNode* pScan;
5667
  SName           table;
5668
} STbCntScanOptInfo;
5669

5670
static bool tbCntScanOptIsEligibleGroupKeys(SNodeList* pGroupKeys) {
782,455✔
5671
  if (NULL == pGroupKeys) {
782,455✔
5672
    return true;
640,077✔
5673
  }
5674

5675
  SNode* pGroupKey = NULL;
142,378✔
5676
  FOREACH(pGroupKey, pGroupKeys) {
142,450!
5677
    SNode* pKey = nodesListGetNode(((SGroupingSetNode*)pGroupKey)->pParameterList, 0);
142,412✔
5678
    if (QUERY_NODE_COLUMN != nodeType(pKey)) {
142,412✔
5679
      return false;
105,486✔
5680
    }
5681
    SColumnNode* pCol = (SColumnNode*)pKey;
36,926✔
5682
    if (0 != strcmp(pCol->colName, "db_name") && 0 != strcmp(pCol->colName, "stable_name")) {
36,926✔
5683
      return false;
36,854✔
5684
    }
5685
  }
5686

5687
  return true;
38✔
5688
}
5689

5690
static bool tbCntScanOptNotNullableExpr(SNode* pNode) {
393,642✔
5691
  if (QUERY_NODE_COLUMN != nodeType(pNode)) {
393,642✔
5692
    return false;
3,580✔
5693
  }
5694
  const char* pColName = ((SColumnNode*)pNode)->colName;
390,062✔
5695
  return 0 == strcmp(pColName, "*") || 0 == strcmp(pColName, "db_name") || 0 == strcmp(pColName, "stable_name") ||
780,123!
5696
         0 == strcmp(pColName, "table_name");
390,061✔
5697
}
5698

5699
static bool tbCntScanOptIsEligibleAggFuncs(SNodeList* pAggFuncs) {
640,122✔
5700
  SNode* pNode = NULL;
640,122✔
5701
  FOREACH(pNode, pAggFuncs) {
640,350!
5702
    SFunctionNode* pFunc = (SFunctionNode*)nodesListGetNode(pAggFuncs, 0);
640,150✔
5703
    if (FUNCTION_TYPE_COUNT != pFunc->funcType ||
640,169✔
5704
        !tbCntScanOptNotNullableExpr(nodesListGetNode(pFunc->pParameterList, 0))) {
393,640✔
5705
      return false;
639,949✔
5706
    }
5707
  }
5708
  return LIST_LENGTH(pAggFuncs) > 0;
200!
5709
}
5710

5711
static bool tbCntScanOptIsEligibleAgg(SAggLogicNode* pAgg) {
782,449✔
5712
  return tbCntScanOptIsEligibleGroupKeys(pAgg->pGroupKeys) && tbCntScanOptIsEligibleAggFuncs(pAgg->pAggFuncs);
782,449✔
5713
}
5714

5715
static bool tbCntScanOptGetColValFromCond(SOperatorNode* pOper, SColumnNode** pCol, SValueNode** pVal) {
×
5716
  if (OP_TYPE_EQUAL != pOper->opType) {
×
5717
    return false;
×
5718
  }
5719

5720
  *pCol = NULL;
×
5721
  *pVal = NULL;
×
5722
  if (QUERY_NODE_COLUMN == nodeType(pOper->pLeft)) {
×
5723
    *pCol = (SColumnNode*)pOper->pLeft;
×
5724
  } else if (QUERY_NODE_VALUE == nodeType(pOper->pLeft)) {
×
5725
    *pVal = (SValueNode*)pOper->pLeft;
×
5726
  }
5727
  if (QUERY_NODE_COLUMN == nodeType(pOper->pRight)) {
×
5728
    *pCol = (SColumnNode*)pOper->pRight;
×
5729
  } else if (QUERY_NODE_VALUE == nodeType(pOper->pRight)) {
×
5730
    *pVal = (SValueNode*)pOper->pRight;
×
5731
  }
5732

5733
  return NULL != *pCol && NULL != *pVal;
×
5734
}
5735

5736
static bool tbCntScanOptIsEligibleLogicCond(STbCntScanOptInfo* pInfo, SLogicConditionNode* pCond) {
×
5737
  if (LOGIC_COND_TYPE_AND != pCond->condType) {
×
5738
    return false;
×
5739
  }
5740

5741
  bool         hasDbCond = false;
×
5742
  bool         hasStbCond = false;
×
5743
  SColumnNode* pCol = NULL;
×
5744
  SValueNode*  pVal = NULL;
×
5745
  SNode*       pNode = NULL;
×
5746
  FOREACH(pNode, pCond->pParameterList) {
×
5747
    if (QUERY_NODE_OPERATOR != nodeType(pNode) || !tbCntScanOptGetColValFromCond((SOperatorNode*)pNode, &pCol, &pVal)) {
×
5748
      return false;
×
5749
    }
5750
    if (!hasDbCond && 0 == strcmp(pCol->colName, "db_name")) {
×
5751
      hasDbCond = true;
×
5752
      tstrncpy(pInfo->table.dbname, pVal->literal, TSDB_DB_NAME_LEN);
×
5753
    } else if (!hasStbCond && 0 == strcmp(pCol->colName, "stable_name")) {
×
5754
      hasStbCond = true;
×
5755
      tstrncpy(pInfo->table.tname, pVal->literal, TSDB_TABLE_NAME_LEN);
×
5756
    } else {
5757
      return false;
×
5758
    }
5759
  }
5760
  return hasDbCond;
×
5761
}
5762

5763
static bool tbCntScanOptIsEligibleOpCond(SOperatorNode* pCond) {
×
5764
  SColumnNode* pCol = NULL;
×
5765
  SValueNode*  pVal = NULL;
×
5766
  if (!tbCntScanOptGetColValFromCond(pCond, &pCol, &pVal)) {
×
5767
    return false;
×
5768
  }
5769
  return 0 == strcmp(pCol->colName, "db_name");
×
5770
}
5771

5772
static bool tbCntScanOptIsEligibleConds(STbCntScanOptInfo* pInfo, SNode* pConditions) {
×
5773
  if (NULL == pConditions) {
×
5774
    return true;
×
5775
  }
5776
  if (LIST_LENGTH(pInfo->pAgg->pGroupKeys) != 0) {
×
5777
    return false;
×
5778
  }
5779
  if (QUERY_NODE_LOGIC_CONDITION == nodeType(pConditions)) {
×
5780
    return tbCntScanOptIsEligibleLogicCond(pInfo, (SLogicConditionNode*)pConditions);
×
5781
  }
5782

5783
  if (QUERY_NODE_OPERATOR == nodeType(pConditions)) {
×
5784
    return tbCntScanOptIsEligibleOpCond((SOperatorNode*)pConditions);
×
5785
  }
5786

5787
  return false;
×
5788
}
5789

5790
static bool tbCntScanOptIsEligibleScan(STbCntScanOptInfo* pInfo) {
228✔
5791
  if (0 != strcmp(pInfo->pScan->tableName.dbname, TSDB_INFORMATION_SCHEMA_DB) ||
228!
5792
      0 != strcmp(pInfo->pScan->tableName.tname, TSDB_INS_TABLE_TABLES) || NULL != pInfo->pScan->pGroupTags ||
228!
5793
      0 != strcmp(pInfo->pScan->tableName.tname, TSDB_INS_DISK_USAGE) ||
226!
5794
      0 != strcmp(pInfo->pScan->tableName.tname, TSDB_INS_TABLE_FILESETS)) {
×
5795
    return false;
228✔
5796
  }
5797
  if (1 == pInfo->pScan->pVgroupList->numOfVgroups && MNODE_HANDLE == pInfo->pScan->pVgroupList->vgroups[0].vgId) {
×
5798
    return false;
×
5799
  }
5800
  return tbCntScanOptIsEligibleConds(pInfo, pInfo->pScan->node.pConditions);
×
5801
}
5802

5803
static bool tbCntScanOptShouldBeOptimized(SLogicNode* pNode, STbCntScanOptInfo* pInfo) {
5,094,009✔
5804
  if (QUERY_NODE_LOGIC_PLAN_AGG != nodeType(pNode) || 1 != LIST_LENGTH(pNode->pChildren) ||
5,094,009!
5805
      QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(nodesListGetNode(pNode->pChildren, 0))) {
1,033,452✔
5806
    return false;
4,311,511✔
5807
  }
5808

5809
  pInfo->pAgg = (SAggLogicNode*)pNode;
782,513✔
5810
  pInfo->pScan = (SScanLogicNode*)nodesListGetNode(pNode->pChildren, 0);
782,513✔
5811
  return tbCntScanOptIsEligibleAgg(pInfo->pAgg) && tbCntScanOptIsEligibleScan(pInfo);
782,523!
5812
}
5813

5814
static int32_t tbCntScanOptCreateTableCountFunc(SNode** ppNode) {
×
5815
  SFunctionNode* pFunc = NULL;
×
5816
  int32_t        code = nodesMakeNode(QUERY_NODE_FUNCTION, (SNode**)&pFunc);
×
5817
  if (NULL == pFunc) {
×
5818
    return code;
×
5819
  }
5820
  tstrncpy(pFunc->functionName, "_table_count", TSDB_FUNC_NAME_LEN);
×
5821
  tstrncpy(pFunc->node.aliasName, "_table_count", TSDB_COL_NAME_LEN);
×
5822
  code = fmGetFuncInfo(pFunc, NULL, 0);
×
5823
  if (TSDB_CODE_SUCCESS != code) {
×
5824
    nodesDestroyNode((SNode*)pFunc);
×
5825
    return code;
×
5826
  }
5827
  *ppNode = (SNode*)pFunc;
×
5828
  return code;
×
5829
}
5830

5831
static int32_t tbCntScanOptRewriteScan(STbCntScanOptInfo* pInfo) {
×
5832
  pInfo->pScan->scanType = SCAN_TYPE_TABLE_COUNT;
×
5833
  tstrncpy(pInfo->pScan->tableName.dbname, pInfo->table.dbname, TSDB_DB_NAME_LEN);
×
5834
  tstrncpy(pInfo->pScan->tableName.tname, pInfo->table.tname, TSDB_TABLE_NAME_LEN);
×
5835
  NODES_DESTORY_LIST(pInfo->pScan->node.pTargets);
×
5836
  NODES_DESTORY_LIST(pInfo->pScan->pScanCols);
×
5837
  NODES_DESTORY_NODE(pInfo->pScan->node.pConditions);
×
5838
  NODES_DESTORY_LIST(pInfo->pScan->pScanPseudoCols);
×
5839
  SNode*  pNew = NULL;
×
5840
  int32_t code = tbCntScanOptCreateTableCountFunc(&pNew);
×
5841
  if (TSDB_CODE_SUCCESS == code) {
×
5842
    code = nodesListMakeStrictAppend(&pInfo->pScan->pScanPseudoCols, pNew);
×
5843
  }
5844
  if (TSDB_CODE_SUCCESS == code) {
×
5845
    code = createColumnByRewriteExpr(nodesListGetNode(pInfo->pScan->pScanPseudoCols, 0), &pInfo->pScan->node.pTargets);
×
5846
  }
5847
  SNode* pGroupKey = NULL;
×
5848
  if (TSDB_CODE_SUCCESS == code) {
×
5849
    FOREACH(pGroupKey, pInfo->pAgg->pGroupKeys) {
×
5850
      SNode* pGroupCol = nodesListGetNode(((SGroupingSetNode*)pGroupKey)->pParameterList, 0);
×
5851
      SNode* pNew = NULL;
×
5852
      code = nodesCloneNode(pGroupCol, &pNew);
×
5853
      if (TSDB_CODE_SUCCESS == code) {
×
5854
        code = nodesListMakeStrictAppend(&pInfo->pScan->pGroupTags, pNew);
×
5855
      }
5856
      if (TSDB_CODE_SUCCESS == code) {
×
5857
        pNew = NULL;
×
5858
        code = nodesCloneNode(pGroupCol, &pNew);
×
5859
        if (TSDB_CODE_SUCCESS == code) {
×
5860
          code = nodesListMakeStrictAppend(&pInfo->pScan->pScanCols, pNew);
×
5861
        }
5862
      }
5863
      if (TSDB_CODE_SUCCESS == code) {
×
5864
        pNew = NULL;
×
5865
        code = nodesCloneNode(pGroupCol, &pNew);
×
5866
        if (TSDB_CODE_SUCCESS == code) {
×
5867
          code = nodesListMakeStrictAppend(&pInfo->pScan->node.pTargets, pNew);
×
5868
        }
5869
      }
5870
      if (TSDB_CODE_SUCCESS != code) {
×
5871
        break;
×
5872
      }
5873
    }
5874
  }
5875
  return code;
×
5876
}
5877

5878
static int32_t tbCntScanOptCreateSumFunc(SFunctionNode* pCntFunc, SNode* pParam, SNode** pOutput) {
×
5879
  SFunctionNode* pFunc = NULL;
×
5880
  int32_t        code = nodesMakeNode(QUERY_NODE_FUNCTION, (SNode**)&pFunc);
×
5881
  if (NULL == pFunc) {
×
5882
    return code;
×
5883
  }
5884
  tstrncpy(pFunc->functionName, "sum", TSDB_FUNC_NAME_LEN);
×
5885
  tstrncpy(pFunc->node.aliasName, pCntFunc->node.aliasName, TSDB_COL_NAME_LEN);
×
5886
  code = createColumnByRewriteExpr(pParam, &pFunc->pParameterList);
×
5887
  if (TSDB_CODE_SUCCESS == code) {
×
5888
    code = fmGetFuncInfo(pFunc, NULL, 0);
×
5889
  }
5890
  if (TSDB_CODE_SUCCESS == code) {
×
5891
    *pOutput = (SNode*)pFunc;
×
5892
  } else {
5893
    nodesDestroyNode((SNode*)pFunc);
×
5894
  }
5895
  return code;
×
5896
}
5897

5898
static int32_t tbCntScanOptRewriteAgg(SAggLogicNode* pAgg) {
×
5899
  SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(pAgg->node.pChildren, 0);
×
5900
  SNode*          pSum = NULL;
×
5901
  int32_t         code = tbCntScanOptCreateSumFunc((SFunctionNode*)nodesListGetNode(pAgg->pAggFuncs, 0),
×
5902
                                                   nodesListGetNode(pScan->pScanPseudoCols, 0), &pSum);
5903
  if (TSDB_CODE_SUCCESS == code) {
×
5904
    NODES_DESTORY_LIST(pAgg->pAggFuncs);
×
5905
    code = nodesListMakeStrictAppend(&pAgg->pAggFuncs, pSum);
×
5906
  }
5907
  if (TSDB_CODE_SUCCESS == code) {
×
5908
    code = partTagsRewriteGroupTagsToFuncs(pScan->pGroupTags, 0, pAgg);
×
5909
  }
5910
  NODES_DESTORY_LIST(pAgg->pGroupKeys);
×
5911
  return code;
×
5912
}
5913

5914
static int32_t tableCountScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
1,442,958✔
5915
  STbCntScanOptInfo info = {0};
1,442,958✔
5916
  if (!optFindEligibleNode(pLogicSubplan->pNode, (FShouldBeOptimized)tbCntScanOptShouldBeOptimized, &info)) {
1,442,958!
5917
    return TSDB_CODE_SUCCESS;
1,443,017✔
5918
  }
5919

UNCOV
5920
  int32_t code = tbCntScanOptRewriteScan(&info);
×
5921
  if (TSDB_CODE_SUCCESS == code) {
×
5922
    code = tbCntScanOptRewriteAgg(info.pAgg);
×
5923
  }
5924
  return code;
×
5925
}
5926

5927
static SSortLogicNode* sortNonPriKeySatisfied(SLogicNode* pNode) {
8,563,634✔
5928
  return NULL;
8,563,634✔
5929
  if (QUERY_NODE_LOGIC_PLAN_SORT != nodeType(pNode)) {
5930
    return NULL;
5931
  }
5932
  SSortLogicNode* pSort = (SSortLogicNode*)pNode;
5933
  if (sortPriKeyOptIsPriKeyOrderBy(pSort->pSortKeys)) {
5934
    return NULL;
5935
  }
5936
  SNode *pSortKeyNode = NULL, *pSortKeyExpr = NULL;
5937
  FOREACH(pSortKeyNode, pSort->pSortKeys) {
5938
    pSortKeyExpr = ((SOrderByExprNode*)pSortKeyNode)->pExpr;
5939
    switch (nodeType(pSortKeyExpr)) {
5940
      case QUERY_NODE_COLUMN:
5941
        break;
5942
      case QUERY_NODE_VALUE:
5943
        continue;
5944
      default:
5945
        return NULL;
5946
    }
5947
  }
5948

5949
  if (!pSortKeyExpr || ((SColumnNode*)pSortKeyExpr)->projIdx != 1 ||
5950
      ((SColumnNode*)pSortKeyExpr)->node.resType.type != TSDB_DATA_TYPE_TIMESTAMP) {
5951
    return NULL;
5952
  }
5953
  return pSort;
5954
}
5955

5956
static bool sortNonPriKeyShouldOptimize(SLogicNode* pNode, void* pInfo) {
8,563,633✔
5957
  SSortLogicNode* pSort = sortNonPriKeySatisfied(pNode);
8,563,633✔
5958
  if (!pSort) return false;
8,563,667!
5959
  SOptimizePKCtx* ctx = pInfo;
×
5960
  ctx->code = nodesListAppend(ctx->pList, (SNode*)pSort);
×
5961
  return false;
×
5962
}
5963

5964
static int32_t sortNonPriKeyOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
1,993,266✔
5965
  SNodeList* pNodeList = NULL;
1,993,266✔
5966
  int32_t    code = nodesMakeList(&pNodeList);
1,993,266✔
5967
  if (TSDB_CODE_SUCCESS != code) {
1,993,342!
5968
    return code;
×
5969
  }
5970
  SOptimizePKCtx ctx = {.pList = pNodeList, .code = 0};
1,993,342✔
5971
  (void)optFindEligibleNode(pLogicSubplan->pNode, sortNonPriKeyShouldOptimize, &ctx);
1,993,342✔
5972
  if (TSDB_CODE_SUCCESS != ctx.code) {
1,993,358✔
5973
    nodesClearList(pNodeList);
3✔
5974
    return code;
×
5975
  }
5976
  SNode* pNode = NULL;
1,993,355✔
5977
  FOREACH(pNode, pNodeList) {
1,993,325!
5978
    SSortLogicNode*   pSort = (SSortLogicNode*)pNode;
×
5979
    SOrderByExprNode* pOrderByExpr = (SOrderByExprNode*)nodesListGetNode(pSort->pSortKeys, 0);
×
5980
    pSort->node.outputTsOrder = pOrderByExpr->order;
×
5981
    optSetParentOrder(pSort->node.pParent, pOrderByExpr->order, NULL);
×
5982
  }
5983
  pCxt->optimized = false;
1,993,325✔
5984
  nodesClearList(pNodeList);
1,993,325✔
5985
  return TSDB_CODE_SUCCESS;
1,993,336✔
5986
}
5987

5988
static bool hashJoinOptShouldBeOptimized(SLogicNode* pNode, void* pCtx) {
8,693,353✔
5989
  bool res = false;
8,693,353✔
5990
  if (QUERY_NODE_LOGIC_PLAN_JOIN != nodeType(pNode)) {
8,693,353✔
5991
    return res;
8,383,761✔
5992
  }
5993

5994
  SJoinLogicNode* pJoin = (SJoinLogicNode*)pNode;
309,592✔
5995
  if (pJoin->joinAlgo != JOIN_ALGO_UNKNOWN) {
309,592✔
5996
    return res;
183,477✔
5997
  }
5998

5999
  if (!pJoin->hashJoinHint) {
126,115!
6000
    goto _return;
126,172✔
6001
  }
6002

6003
  if ((JOIN_STYPE_NONE != pJoin->subType && JOIN_STYPE_OUTER != pJoin->subType) || JOIN_TYPE_FULL == pJoin->joinType ||
×
6004
      pNode->pChildren->length != 2) {
×
6005
    goto _return;
×
6006
  }
6007

6008
  res = true;
×
6009

6010
_return:
126,115✔
6011

6012
  if (!res && DATA_ORDER_LEVEL_NONE == pJoin->node.requireDataOrder) {
126,115!
6013
    pJoin->node.requireDataOrder = DATA_ORDER_LEVEL_GLOBAL;
320✔
6014
    int32_t* pCode = pCtx;
320✔
6015
    int32_t  code = adjustLogicNodeDataRequirement(pNode, pJoin->node.requireDataOrder);
320✔
6016
    if (TSDB_CODE_SUCCESS != code) {
320!
6017
      *pCode = code;
×
6018
    }
6019
  }
6020

6021
  return res;
126,115✔
6022
}
6023

6024
static int32_t hashJoinOptSplitPrimFromLogicCond(SNode** pCondition, SNode** pPrimaryKeyCond) {
×
6025
  SLogicConditionNode* pLogicCond = (SLogicConditionNode*)(*pCondition);
×
6026
  int32_t              code = TSDB_CODE_SUCCESS;
×
6027
  SNodeList*           pPrimaryKeyConds = NULL;
×
6028
  SNode*               pCond = NULL;
×
6029
  WHERE_EACH(pCond, pLogicCond->pParameterList) {
×
6030
    bool result = false;
×
6031
    code = filterIsMultiTableColsCond(pCond, &result);
×
6032
    if (TSDB_CODE_SUCCESS != code) {
×
6033
      break;
×
6034
    }
6035

6036
    if (result || COND_TYPE_PRIMARY_KEY != filterClassifyCondition(pCond)) {
×
6037
      WHERE_NEXT;
×
6038
      continue;
×
6039
    }
6040

6041
    SNode* pNew = NULL;
×
6042
    code = nodesCloneNode(pCond, &pNew);
×
6043
    if (TSDB_CODE_SUCCESS == code) {
×
6044
      code = nodesListMakeAppend(&pPrimaryKeyConds, pNew);
×
6045
    }
6046
    if (TSDB_CODE_SUCCESS != code) {
×
6047
      break;
×
6048
    }
6049

6050
    ERASE_NODE(pLogicCond->pParameterList);
×
6051
  }
6052

6053
  SNode* pTempPrimaryKeyCond = NULL;
×
6054
  if (TSDB_CODE_SUCCESS == code && pPrimaryKeyConds) {
×
6055
    code = nodesMergeConds(&pTempPrimaryKeyCond, &pPrimaryKeyConds);
×
6056
  }
6057

6058
  if (TSDB_CODE_SUCCESS == code && pTempPrimaryKeyCond) {
×
6059
    *pPrimaryKeyCond = pTempPrimaryKeyCond;
×
6060

6061
    if (pLogicCond->pParameterList->length <= 0) {
×
6062
      nodesDestroyNode(*pCondition);
×
6063
      *pCondition = NULL;
×
6064
    }
6065
  } else {
6066
    nodesDestroyList(pPrimaryKeyConds);
×
6067
    nodesDestroyNode(pTempPrimaryKeyCond);
×
6068
  }
6069

6070
  return code;
×
6071
}
6072

6073
int32_t hashJoinOptSplitPrimCond(SNode** pCondition, SNode** pPrimaryKeyCond) {
×
6074
  if (QUERY_NODE_LOGIC_CONDITION == nodeType(*pCondition)) {
×
6075
    if (LOGIC_COND_TYPE_AND == ((SLogicConditionNode*)*pCondition)->condType) {
×
6076
      return hashJoinOptSplitPrimFromLogicCond(pCondition, pPrimaryKeyCond);
×
6077
    }
6078

6079
    return TSDB_CODE_SUCCESS;
×
6080
  }
6081

6082
  bool    needOutput = false;
×
6083
  bool    result = false;
×
6084
  int32_t code = filterIsMultiTableColsCond(*pCondition, &result);
×
6085
  if (TSDB_CODE_SUCCESS != code) {
×
6086
    return code;
×
6087
  }
6088
  if (result) {
×
6089
    return TSDB_CODE_SUCCESS;
×
6090
  }
6091

6092
  EConditionType type = filterClassifyCondition(*pCondition);
×
6093
  if (COND_TYPE_PRIMARY_KEY == type) {
×
6094
    *pPrimaryKeyCond = *pCondition;
×
6095
    *pCondition = NULL;
×
6096
  }
6097

6098
  return TSDB_CODE_SUCCESS;
×
6099
}
6100

6101
static int32_t hashJoinOptRewriteJoin(SOptimizeContext* pCxt, SLogicNode* pNode, SLogicSubplan* pLogicSubplan) {
×
6102
  SJoinLogicNode* pJoin = (SJoinLogicNode*)pNode;
×
6103
  int32_t         code = TSDB_CODE_SUCCESS;
×
6104

6105
  pJoin->joinAlgo = JOIN_ALGO_HASH;
×
6106

6107
  if (NULL != pJoin->pColOnCond) {
×
6108
#if 0  
6109
    EJoinType t = pJoin->joinType;
6110
    EJoinSubType s = pJoin->subType;
6111

6112
    pJoin->joinType = JOIN_TYPE_INNER;
6113
    pJoin->subType = JOIN_STYPE_NONE;
6114
    
6115
    code = pdcJoinSplitCond(pJoin, &pJoin->pColOnCond, NULL, &pJoin->pLeftOnCond, &pJoin->pRightOnCond, true);
6116

6117
    pJoin->joinType = t;
6118
    pJoin->subType = s;
6119

6120
    if (TSDB_CODE_SUCCESS != code) {
6121
      return code;
6122
    }
6123

6124
    STimeWindow ltimeRange = TSWINDOW_INITIALIZER;
6125
    STimeWindow rtimeRange = TSWINDOW_INITIALIZER;
6126
    SNode* pPrimaryKeyCond = NULL;
6127
    if (NULL != pJoin->pLeftOnCond) {
6128
      hashJoinOptSplitPrimCond(&pJoin->pLeftOnCond, &pPrimaryKeyCond);
6129
      if (NULL != pPrimaryKeyCond) {
6130
        bool isStrict = false;
6131
        code = getTimeRangeFromNode(&pPrimaryKeyCond, &ltimeRange, &isStrict);
6132
        nodesDestroyNode(pPrimaryKeyCond);
6133
      }
6134
    }
6135

6136
    if (TSDB_CODE_SUCCESS != code) {
6137
      return code;
6138
    }
6139
    
6140
    if (NULL != pJoin->pRightOnCond) {
6141
      pPrimaryKeyCond = NULL;
6142
      hashJoinOptSplitPrimCond(&pJoin->pRightOnCond, &pPrimaryKeyCond);
6143
      if (NULL != pPrimaryKeyCond) {
6144
        bool isStrict = false;
6145
        code = getTimeRangeFromNode(&pPrimaryKeyCond, &rtimeRange, &isStrict);
6146
        nodesDestroyNode(pPrimaryKeyCond);
6147
      }
6148
    }    
6149

6150
    if (TSDB_CODE_SUCCESS != code) {
6151
      return code;
6152
    }
6153

6154
    if (TSWINDOW_IS_EQUAL(ltimeRange, TSWINDOW_INITIALIZER)) {
6155
      pJoin->timeRange.skey = rtimeRange.skey;
6156
      pJoin->timeRange.ekey = rtimeRange.ekey;
6157
    } else if (TSWINDOW_IS_EQUAL(rtimeRange, TSWINDOW_INITIALIZER)) {
6158
      pJoin->timeRange.skey = ltimeRange.skey;
6159
      pJoin->timeRange.ekey = ltimeRange.ekey;
6160
    } else if (ltimeRange.ekey < rtimeRange.skey || ltimeRange.skey > rtimeRange.ekey) {
6161
      pJoin->timeRange = TSWINDOW_DESC_INITIALIZER;
6162
    } else {
6163
      pJoin->timeRange.skey = TMAX(ltimeRange.skey, rtimeRange.skey);
6164
      pJoin->timeRange.ekey = TMIN(ltimeRange.ekey, rtimeRange.ekey);
6165
    }
6166
#else
6167
    SNode* pPrimaryKeyCond = NULL;
×
6168
    code = hashJoinOptSplitPrimCond(&pJoin->pColOnCond, &pPrimaryKeyCond);
×
6169
    if (TSDB_CODE_SUCCESS != code) {
×
6170
      return code;
×
6171
    }
6172
    if (NULL != pPrimaryKeyCond) {
×
6173
      bool isStrict = false;
×
6174
      code = getTimeRangeFromNode(&pPrimaryKeyCond, &pJoin->timeRange, &isStrict);
×
6175
      nodesDestroyNode(pPrimaryKeyCond);
×
6176
    }
6177
#endif
6178
  } else {
6179
    TAOS_SET_OBJ_ALIGNED(&pJoin->timeRange, TSWINDOW_INITIALIZER);
×
6180
  }
6181

6182
#if 0
6183
  if (NULL != pJoin->pTagOnCond && !TSWINDOW_IS_EQUAL(pJoin->timeRange, TSWINDOW_DESC_INITIALIZER)) {
6184
    EJoinType t = pJoin->joinType;
6185
    EJoinSubType s = pJoin->subType;
6186
    SNode*  pLeftChildCond = NULL;
6187
    SNode*  pRightChildCond = NULL;
6188

6189
    pJoin->joinType = JOIN_TYPE_INNER;
6190
    pJoin->subType = JOIN_STYPE_NONE;
6191
    
6192
    code = pdcJoinSplitCond(pJoin, &pJoin->pTagOnCond, NULL, &pLeftChildCond, &pRightChildCond, true);
6193

6194
    pJoin->joinType = t;
6195
    pJoin->subType = s;
6196

6197
    if (TSDB_CODE_SUCCESS == code) {
6198
      code = mergeJoinConds(&pJoin->pLeftOnCond, &pLeftChildCond);
6199
    }
6200
    if (TSDB_CODE_SUCCESS == code) {
6201
      code = mergeJoinConds(&pJoin->pRightOnCond, &pRightChildCond);
6202
    }
6203

6204
    nodesDestroyNode(pLeftChildCond);
6205
    nodesDestroyNode(pRightChildCond);
6206

6207
    if (TSDB_CODE_SUCCESS != code) {
6208
      return code;
6209
    }
6210
  }
6211
#endif
6212

6213
  if (!TSWINDOW_IS_EQUAL(pJoin->timeRange, TSWINDOW_DESC_INITIALIZER)) {
×
6214
    SNode* pChild = NULL;
×
6215
    FOREACH(pChild, pJoin->node.pChildren) {
×
6216
      if (QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(pChild)) {
×
6217
        continue;
×
6218
      }
6219

6220
      SScanLogicNode* pScan = (SScanLogicNode*)pChild;
×
6221
      if (TSWINDOW_IS_EQUAL(pScan->scanRange, TSWINDOW_INITIALIZER)) {
×
6222
        continue;
×
6223
      } else if (pJoin->timeRange.ekey < pScan->scanRange.skey || pJoin->timeRange.skey > pScan->scanRange.ekey) {
×
6224
        TAOS_SET_OBJ_ALIGNED(&pJoin->timeRange, TSWINDOW_DESC_INITIALIZER);
×
6225
        break;
×
6226
      } else {
6227
        pJoin->timeRange.skey = TMAX(pJoin->timeRange.skey, pScan->scanRange.skey);
×
6228
        pJoin->timeRange.ekey = TMIN(pJoin->timeRange.ekey, pScan->scanRange.ekey);
×
6229
      }
6230
    }
6231
  }
6232

6233
  pJoin->timeRangeTarget = 0;
×
6234

6235
  if (!TSWINDOW_IS_EQUAL(pJoin->timeRange, TSWINDOW_INITIALIZER)) {
×
6236
    SNode*  pChild = NULL;
×
6237
    int32_t timeRangeTarget = 1;
×
6238
    FOREACH(pChild, pJoin->node.pChildren) {
×
6239
      if (QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(pChild)) {
×
6240
        timeRangeTarget++;
×
6241
        continue;
×
6242
      }
6243

6244
      SScanLogicNode* pScan = (SScanLogicNode*)pChild;
×
6245
      if (TSWINDOW_IS_EQUAL(pScan->scanRange, pJoin->timeRange)) {
×
6246
        timeRangeTarget++;
×
6247
        continue;
×
6248
      }
6249

6250
      bool replaced = false;
×
6251
      switch (pJoin->joinType) {
×
6252
        case JOIN_TYPE_INNER:
×
6253
          pScan->scanRange.skey = pJoin->timeRange.skey;
×
6254
          pScan->scanRange.ekey = pJoin->timeRange.ekey;
×
6255
          replaced = true;
×
6256
          break;
×
6257
        case JOIN_TYPE_LEFT:
×
6258
          if (2 == timeRangeTarget) {
×
6259
            pScan->scanRange.skey = pJoin->timeRange.skey;
×
6260
            pScan->scanRange.ekey = pJoin->timeRange.ekey;
×
6261
            replaced = true;
×
6262
          }
6263
          break;
×
6264
        case JOIN_TYPE_RIGHT:
×
6265
          if (1 == timeRangeTarget) {
×
6266
            pScan->scanRange.skey = pJoin->timeRange.skey;
×
6267
            pScan->scanRange.ekey = pJoin->timeRange.ekey;
×
6268
            replaced = true;
×
6269
          }
6270
          break;
×
6271
        default:
×
6272
          break;
×
6273
      }
6274

6275
      if (replaced) {
×
6276
        timeRangeTarget++;
×
6277
        continue;
×
6278
      }
6279

6280
      pJoin->timeRangeTarget += timeRangeTarget;
×
6281
      timeRangeTarget++;
×
6282
    }
6283
  }
6284

6285
  pCxt->optimized = true;
×
6286
  OPTIMIZE_FLAG_SET_MASK(pJoin->node.optimizedFlag, OPTIMIZE_FLAG_STB_JOIN);
×
6287

6288
  return TSDB_CODE_SUCCESS;
×
6289
}
6290

6291
static int32_t hashJoinOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
2,012,727✔
6292
  int32_t     code = 0;
2,012,727✔
6293
  SLogicNode* pNode = optFindPossibleNode(pLogicSubplan->pNode, hashJoinOptShouldBeOptimized, &code);
2,012,727✔
6294
  if (TSDB_CODE_SUCCESS != code) {
2,012,848!
6295
    return code;
×
6296
  }
6297
  if (NULL == pNode) {
2,012,848✔
6298
    return TSDB_CODE_SUCCESS;
2,012,840✔
6299
  }
6300

6301
  return hashJoinOptRewriteJoin(pCxt, pNode, pLogicSubplan);
8✔
6302
}
6303

6304
static bool stbJoinOptShouldBeOptimized(SLogicNode* pNode, void* pCtx) {
8,655,480✔
6305
  if (QUERY_NODE_LOGIC_PLAN_JOIN != nodeType(pNode) ||
8,655,480✔
6306
      OPTIMIZE_FLAG_TEST_MASK(pNode->optimizedFlag, OPTIMIZE_FLAG_STB_JOIN)) {
309,649✔
6307
    return false;
8,449,219✔
6308
  }
6309

6310
  SJoinLogicNode* pJoin = (SJoinLogicNode*)pNode;
206,261✔
6311
  if (pJoin->joinAlgo == JOIN_ALGO_UNKNOWN) {
206,261✔
6312
    pJoin->joinAlgo = JOIN_ALGO_MERGE;
126,172✔
6313
  }
6314

6315
  if (JOIN_STYPE_NONE != pJoin->subType || pJoin->isSingleTableJoin || NULL == pJoin->pTagEqCond ||
206,261✔
6316
      pNode->pChildren->length != 2 || pJoin->isLowLevelJoin) {
18,999!
6317
    return false;
187,293✔
6318
  }
6319

6320
  SNode* pLeft = nodesListGetNode(pJoin->node.pChildren, 0);
18,968✔
6321
  SNode* pRight = nodesListGetNode(pJoin->node.pChildren, 1);
18,968✔
6322
  if (QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(pLeft) || QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(pRight)) {
18,968!
6323
    return false;
4✔
6324
  }
6325

6326
  return true;
18,964✔
6327
}
6328

6329
int32_t stbJoinOptAddFuncToScanNode(char* funcName, SScanLogicNode* pScan) {
75,856✔
6330
  SFunctionNode* pUidFunc = NULL;
75,856✔
6331
  int32_t        code = createFunction(funcName, NULL, &pUidFunc);
75,856✔
6332
  if (TSDB_CODE_SUCCESS != code) {
75,856!
6333
    return code;
×
6334
  }
6335
  snprintf(pUidFunc->node.aliasName, sizeof(pUidFunc->node.aliasName), "%s.%p", pUidFunc->functionName, pUidFunc);
75,856✔
6336
  code = nodesListStrictAppend(pScan->pScanPseudoCols, (SNode*)pUidFunc);
75,856✔
6337
  if (TSDB_CODE_SUCCESS == code) {
75,856!
6338
    code = createColumnByRewriteExpr((SNode*)pUidFunc, &pScan->node.pTargets);
75,856✔
6339
  }
6340
  return code;
75,856✔
6341
}
6342

6343
int32_t stbJoinOptRewriteToTagScan(SLogicNode* pJoin, SNode* pNode) {
37,928✔
6344
  SScanLogicNode* pScan = (SScanLogicNode*)pNode;
37,928✔
6345
  SJoinLogicNode* pJoinNode = (SJoinLogicNode*)pJoin;
37,928✔
6346

6347
  pScan->scanType = SCAN_TYPE_TAG;
37,928✔
6348
  NODES_DESTORY_LIST(pScan->pScanCols);
37,928✔
6349
  NODES_DESTORY_NODE(pScan->node.pConditions);
37,928✔
6350
  pScan->node.requireDataOrder = DATA_ORDER_LEVEL_NONE;
37,928✔
6351
  pScan->node.resultDataOrder = DATA_ORDER_LEVEL_NONE;
37,928✔
6352

6353
  SNodeList* pTags = NULL;
37,928✔
6354
  int32_t    code = nodesMakeList(&pTags);
37,928✔
6355
  if (TSDB_CODE_SUCCESS == code) {
37,928!
6356
    code = nodesCollectColumnsFromNode(pJoinNode->pTagEqCond, NULL, COLLECT_COL_TYPE_TAG, &pTags);
37,928✔
6357
  }
6358
  if (TSDB_CODE_SUCCESS == code) {
37,928!
6359
    code = nodesCollectColumnsFromNode(pJoinNode->pTagOnCond, NULL, COLLECT_COL_TYPE_TAG, &pTags);
37,928✔
6360
  }
6361
  if (TSDB_CODE_SUCCESS == code) {
37,928!
6362
    SNode* pTarget = NULL;
37,928✔
6363
    SNode* pTag = NULL;
37,928✔
6364
    bool   found = false;
37,928✔
6365
    WHERE_EACH(pTarget, pScan->node.pTargets) {
194,572!
6366
      found = false;
156,644✔
6367
      SColumnNode* pTargetCol = (SColumnNode*)pTarget;
156,644✔
6368
      FOREACH(pTag, pTags) {
413,528!
6369
        SColumnNode* pTagCol = (SColumnNode*)pTag;
294,892✔
6370
        if (0 == taosStrcasecmp(pTargetCol->node.aliasName, pTagCol->colName) &&
294,892✔
6371
            0 == taosStrcasecmp(pTargetCol->tableAlias, pTagCol->tableAlias)) {
56,962✔
6372
          found = true;
38,008✔
6373
          break;
38,008✔
6374
        }
6375
      }
6376
      if (!found) {
156,644✔
6377
        ERASE_NODE(pScan->node.pTargets);
118,636✔
6378
      } else {
6379
        WHERE_NEXT;
38,008✔
6380
      }
6381
    }
6382
  }
6383
  if (TSDB_CODE_SUCCESS == code) {
37,928!
6384
    code = stbJoinOptAddFuncToScanNode("_tbuid", pScan);
37,928✔
6385
  }
6386
  if (TSDB_CODE_SUCCESS == code) {
37,928!
6387
    code = stbJoinOptAddFuncToScanNode("_vgid", pScan);
37,928✔
6388
  }
6389

6390
  if (TSDB_CODE_SUCCESS == code) {
37,928!
6391
    code = tagScanSetExecutionMode(pScan);
37,928✔
6392
  }
6393

6394
  if (code) {
37,928!
6395
    nodesDestroyList(pTags);
×
6396
  }
6397

6398
  return code;
37,928✔
6399
}
6400

6401
static int32_t stbJoinOptCreateTagScanNode(SLogicNode* pJoin, SNodeList** ppList) {
18,964✔
6402
  SNodeList* pList = NULL;
18,964✔
6403
  int32_t    code = nodesCloneList(pJoin->pChildren, &pList);
18,964✔
6404
  if (NULL == pList) {
18,964!
6405
    return code;
×
6406
  }
6407

6408
  SNode* pNode = NULL;
18,964✔
6409
  SName* pPrev = NULL;
18,964✔
6410
  FOREACH(pNode, pList) {
56,892!
6411
    code = stbJoinOptRewriteToTagScan(pJoin, pNode);
37,928✔
6412
    if (code) {
37,928!
6413
      break;
×
6414
    }
6415

6416
    SScanLogicNode* pScan = (SScanLogicNode*)pNode;
37,928✔
6417
    // If join node's two children are scan on same table and on only one vgroup, don't need to split. Vice versa.
6418
    if (pScan->pVgroupList && 1 == pScan->pVgroupList->numOfVgroups) {
37,928!
6419
      if (NULL == pPrev || 0 == strcmp(pPrev->dbname, pScan->tableName.dbname)) {
78✔
6420
        pPrev = &pScan->tableName;
62✔
6421
        continue;
62✔
6422
      }
6423

6424
      pScan->needSplit = true;
16✔
6425
    }
6426
  }
6427

6428
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6429
    *ppList = pList;
18,964✔
6430
  } else {
6431
    nodesDestroyList(pList);
×
6432
  }
6433

6434
  return code;
18,964✔
6435
}
6436

6437
static int32_t stbJoinOptCreateTagHashJoinNode(SLogicNode* pOrig, SNodeList* pChildren, SLogicNode** ppLogic) {
18,964✔
6438
  SJoinLogicNode* pOrigJoin = (SJoinLogicNode*)pOrig;
18,964✔
6439
  SJoinLogicNode* pJoin = NULL;
18,964✔
6440
  int32_t         code = nodesMakeNode(QUERY_NODE_LOGIC_PLAN_JOIN, (SNode**)&pJoin);
18,964✔
6441
  if (NULL == pJoin) {
18,964!
6442
    return code;
×
6443
  }
6444

6445
  pJoin->joinType = pOrigJoin->joinType;
18,964✔
6446
  pJoin->subType = pOrigJoin->subType;
18,964✔
6447
  pJoin->joinAlgo = JOIN_ALGO_HASH;
18,964✔
6448
  pJoin->isSingleTableJoin = pOrigJoin->isSingleTableJoin;
18,964✔
6449
  pJoin->hasSubQuery = pOrigJoin->hasSubQuery;
18,964✔
6450
  pJoin->node.inputTsOrder = pOrigJoin->node.inputTsOrder;
18,964✔
6451
  pJoin->node.groupAction = pOrigJoin->node.groupAction;
18,964✔
6452
  pJoin->node.requireDataOrder = DATA_ORDER_LEVEL_NONE;
18,964✔
6453
  pJoin->node.resultDataOrder = DATA_ORDER_LEVEL_NONE;
18,964✔
6454
  code = nodesCloneNode(pOrigJoin->pTagEqCond, &pJoin->pTagEqCond);
18,964✔
6455
  if (TSDB_CODE_SUCCESS != code) {
18,964!
6456
    nodesDestroyNode((SNode*)pJoin);
×
6457
    return code;
×
6458
  }
6459
  code = nodesCloneNode(pOrigJoin->pTagOnCond, &pJoin->pTagOnCond);
18,964✔
6460
  if (TSDB_CODE_SUCCESS != code) {
18,964!
6461
    nodesDestroyNode((SNode*)pJoin);
×
6462
    return code;
×
6463
  }
6464

6465
  pJoin->node.pChildren = pChildren;
18,964✔
6466

6467
  SNode* pNode = NULL;
18,964✔
6468
  FOREACH(pNode, pChildren) {
56,892!
6469
    SScanLogicNode* pScan = (SScanLogicNode*)pNode;
37,928✔
6470
    SNode*          pCol = NULL;
37,928✔
6471
    FOREACH(pCol, pScan->pScanPseudoCols) {
198,064!
6472
      if (QUERY_NODE_FUNCTION == nodeType(pCol) && (((SFunctionNode*)pCol)->funcType == FUNCTION_TYPE_TBUID ||
160,136✔
6473
                                                    ((SFunctionNode*)pCol)->funcType == FUNCTION_TYPE_VGID)) {
38,012✔
6474
        code = createColumnByRewriteExpr(pCol, &pJoin->node.pTargets);
75,856✔
6475
        if (code) {
75,856!
6476
          break;
×
6477
        }
6478
      }
6479
    }
6480
    if (code) {
37,928!
6481
      break;
×
6482
    }
6483
    pScan->node.pParent = (SLogicNode*)pJoin;
37,928✔
6484
  }
6485

6486
  SNodeList* pCols = NULL;
18,964✔
6487
  code = nodesCollectColumnsFromNode(pJoin->pFullOnCond, NULL, COLLECT_COL_TYPE_ALL, &pCols);
18,964✔
6488

6489
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6490
    FOREACH(pNode, pCols) {
18,964!
6491
      code = createColumnByRewriteExpr(pNode, &pJoin->node.pTargets);
×
6492
      if (code) {
×
6493
        break;
×
6494
      }
6495
    }
6496
  }
6497
  nodesDestroyList(pCols);
18,964✔
6498

6499
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6500
    *ppLogic = (SLogicNode*)pJoin;
18,964✔
6501
    OPTIMIZE_FLAG_SET_MASK(pJoin->node.optimizedFlag, OPTIMIZE_FLAG_STB_JOIN);
18,964✔
6502
  } else {
6503
    nodesDestroyNode((SNode*)pJoin);
×
6504
  }
6505

6506
  return code;
18,964✔
6507
}
6508

6509
static int32_t stbJoinOptCreateTableScanNodes(SLogicNode* pJoin, SNodeList** ppList, bool* srcScan) {
18,964✔
6510
  SNodeList* pList = NULL;
18,964✔
6511
  int32_t    code = nodesCloneList(pJoin->pChildren, &pList);
18,964✔
6512
  if (NULL == pList) {
18,964!
6513
    return code;
×
6514
  }
6515

6516
  int32_t i = 0;
18,964✔
6517
  SName* pPrev = NULL;
18,964✔
6518
  SNode*  pNode = NULL;
18,964✔
6519
  FOREACH(pNode, pList) {
56,892!
6520
    SScanLogicNode* pScan = (SScanLogicNode*)pNode;
37,928✔
6521
    // code = stbJoinOptAddFuncToScanNode("_tbuid", pScan);
6522
    // if (code) {
6523
    //   break;
6524
    // }
6525

6526
    nodesDestroyNode(pScan->pTagCond);
37,928✔
6527
    pScan->pTagCond = NULL;
37,928✔
6528
    nodesDestroyNode(pScan->pTagIndexCond);
37,928✔
6529
    pScan->pTagIndexCond = NULL;
37,928✔
6530

6531
    pScan->node.dynamicOp = true;
37,928✔
6532
    *(srcScan + i++) = pScan->pVgroupList->numOfVgroups <= 1;
37,928✔
6533

6534
    pScan->scanType = SCAN_TYPE_TABLE;
37,928✔
6535

6536
    if (pScan->pVgroupList && 1 == pScan->pVgroupList->numOfVgroups) {
37,928!
6537
      if (NULL == pPrev || 0 == strcmp(pPrev->dbname, pScan->tableName.dbname)) {
78✔
6538
        pPrev = &pScan->tableName;
62✔
6539
        continue;
62✔
6540
      }
6541

6542
      pScan->needSplit = true;
16✔
6543
      *(srcScan + i - 1) = false;
16✔
6544
    }
6545
  }
6546

6547
  *ppList = pList;
18,964✔
6548

6549
  return code;
18,964✔
6550
}
6551

6552
static int32_t stbJoinOptCreateGroupCacheNode(SLogicNode* pRoot, SNodeList* pChildren, SLogicNode** ppLogic) {
18,964✔
6553
  int32_t               code = TSDB_CODE_SUCCESS;
18,964✔
6554
  SGroupCacheLogicNode* pGrpCache = NULL;
18,964✔
6555
  code = nodesMakeNode(QUERY_NODE_LOGIC_PLAN_GROUP_CACHE, (SNode**)&pGrpCache);
18,964✔
6556
  if (NULL == pGrpCache) {
18,964!
6557
    return code;
×
6558
  }
6559

6560
  // pGrpCache->node.dynamicOp = true;
6561
  pGrpCache->grpColsMayBeNull = false;
18,964✔
6562
  pGrpCache->grpByUid = true;
18,964✔
6563
  pGrpCache->batchFetch = getBatchScanOptionFromHint(pRoot->pHint);
18,964✔
6564
  pGrpCache->node.pChildren = pChildren;
18,964✔
6565
  pGrpCache->node.pTargets = NULL;
18,964✔
6566
  code = nodesMakeList(&pGrpCache->node.pTargets);
18,964✔
6567
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6568
    SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(pChildren, 0);
18,964✔
6569
    SNodeList*      pNewList = NULL;
18,964✔
6570
    code = nodesCloneList(pScan->node.pTargets, &pNewList);
18,964✔
6571
    if (TSDB_CODE_SUCCESS == code) {
18,964!
6572
      code = nodesListStrictAppendList(pGrpCache->node.pTargets, pNewList);
18,964✔
6573
    }
6574
  }
6575

6576
  SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(pChildren, 0);
18,964✔
6577
  SNode*          pCol = NULL;
18,964✔
6578
  FOREACH(pCol, pScan->pScanPseudoCols) {
60,994!
6579
    if (QUERY_NODE_FUNCTION == nodeType(pCol) && (((SFunctionNode*)pCol)->funcType == FUNCTION_TYPE_TBUID ||
42,030!
6580
                                                  ((SFunctionNode*)pCol)->funcType == FUNCTION_TYPE_VGID)) {
44!
6581
      code = createColumnByRewriteExpr(pCol, &pGrpCache->pGroupCols);
×
6582
      if (code) {
×
6583
        break;
×
6584
      }
6585
    }
6586
  }
6587

6588
  bool   hasCond = false;
18,964✔
6589
  SNode* pNode = NULL;
18,964✔
6590
  FOREACH(pNode, pChildren) {
56,892!
6591
    SScanLogicNode* pScan = (SScanLogicNode*)pNode;
37,928✔
6592
    if (pScan->node.pConditions) {
37,928✔
6593
      hasCond = true;
73✔
6594
    }
6595
    pScan->node.pParent = (SLogicNode*)pGrpCache;
37,928✔
6596
  }
6597
  pGrpCache->globalGrp = false;
18,964✔
6598

6599
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6600
    *ppLogic = (SLogicNode*)pGrpCache;
18,964✔
6601
  } else {
6602
    nodesDestroyNode((SNode*)pGrpCache);
×
6603
  }
6604

6605
  return code;
18,964✔
6606
}
6607

6608
static void stbJoinOptRemoveTagEqCond(SJoinLogicNode* pJoin) {
18,964✔
6609
  if (QUERY_NODE_OPERATOR == nodeType(pJoin->pFullOnCond) && nodesEqualNode(pJoin->pFullOnCond, pJoin->pTagEqCond)) {
18,964!
6610
    NODES_DESTORY_NODE(pJoin->pFullOnCond);
2,151✔
6611
    return;
2,151✔
6612
  }
6613
  if (QUERY_NODE_LOGIC_CONDITION == nodeType(pJoin->pFullOnCond)) {
16,813!
6614
    SLogicConditionNode* pLogic = (SLogicConditionNode*)pJoin->pFullOnCond;
16,813✔
6615
    SNode*               pNode = NULL;
16,813✔
6616
    FOREACH(pNode, pLogic->pParameterList) {
16,906!
6617
      if (nodesEqualNode(pNode, pJoin->pTagEqCond)) {
16,862✔
6618
        ERASE_NODE(pLogic->pParameterList);
16,769✔
6619
        break;
16,769✔
6620
      } else if (QUERY_NODE_LOGIC_CONDITION == nodeType(pJoin->pTagEqCond)) {
93✔
6621
        SLogicConditionNode* pTags = (SLogicConditionNode*)pJoin->pTagEqCond;
92✔
6622
        SNode*               pTag = NULL;
92✔
6623
        bool                 found = false;
92✔
6624
        FOREACH(pTag, pTags->pParameterList) {
144!
6625
          if (nodesEqualNode(pTag, pNode)) {
140✔
6626
            found = true;
88✔
6627
            break;
88✔
6628
          }
6629
        }
6630
        if (found) {
92✔
6631
          ERASE_NODE(pLogic->pParameterList);
88✔
6632
        }
6633
      }
6634
    }
6635

6636
    if (pLogic->pParameterList->length <= 0) {
16,813✔
6637
      NODES_DESTORY_NODE(pJoin->pFullOnCond);
40✔
6638
    }
6639
  }
6640
}
6641

6642
static int32_t stbJoinOptCreateMergeJoinNode(SLogicNode* pOrig, SLogicNode* pChild, SLogicNode** ppLogic) {
18,964✔
6643
  SJoinLogicNode* pOrigJoin = (SJoinLogicNode*)pOrig;
18,964✔
6644
  SJoinLogicNode* pJoin = NULL;
18,964✔
6645
  int32_t         code = nodesCloneNode((SNode*)pOrig, (SNode**)&pJoin);
18,964✔
6646
  if (NULL == pJoin) {
18,964!
6647
    return code;
×
6648
  }
6649

6650
  pJoin->joinAlgo = JOIN_ALGO_MERGE;
18,964✔
6651
  // pJoin->node.dynamicOp = true;
6652

6653
  stbJoinOptRemoveTagEqCond(pJoin);
18,964✔
6654
  NODES_DESTORY_NODE(pJoin->pTagEqCond);
18,964✔
6655

6656
  SNode* pNode = NULL;
18,964✔
6657
  FOREACH(pNode, pJoin->node.pChildren) { ERASE_NODE(pJoin->node.pChildren); }
56,892!
6658
  code = nodesListStrictAppend(pJoin->node.pChildren, (SNode*)pChild);
18,964✔
6659
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6660
    pChild->pParent = (SLogicNode*)pJoin;
18,964✔
6661
    *ppLogic = (SLogicNode*)pJoin;
18,964✔
6662
    OPTIMIZE_FLAG_SET_MASK(pJoin->node.optimizedFlag, OPTIMIZE_FLAG_STB_JOIN);
18,964✔
6663
  } else {
6664
    nodesDestroyNode((SNode*)pJoin);
×
6665
  }
6666

6667
  return code;
18,964✔
6668
}
6669

6670
static int32_t stbJoinOptCreateDynQueryCtrlNode(SLogicNode* pRoot, SLogicNode* pPrev, SLogicNode* pPost, bool* srcScan,
18,964✔
6671
                                                SLogicNode** ppDynNode) {
6672
  int32_t                 code = TSDB_CODE_SUCCESS;
18,964✔
6673
  SDynQueryCtrlLogicNode* pDynCtrl = NULL;
18,964✔
6674
  code = nodesMakeNode(QUERY_NODE_LOGIC_PLAN_DYN_QUERY_CTRL, (SNode**)&pDynCtrl);
18,964✔
6675
  if (NULL == pDynCtrl) {
18,964!
6676
    return code;
×
6677
  }
6678

6679
  pDynCtrl->qType = DYN_QTYPE_STB_HASH;
18,964✔
6680
  pDynCtrl->stbJoin.batchFetch = getBatchScanOptionFromHint(pRoot->pHint);
18,964✔
6681
  memcpy(pDynCtrl->stbJoin.srcScan, srcScan, sizeof(pDynCtrl->stbJoin.srcScan));
18,964✔
6682

6683
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6684
    pDynCtrl->node.pChildren = NULL;
18,964✔
6685
    code = nodesMakeList(&pDynCtrl->node.pChildren);
18,964✔
6686
  }
6687

6688
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6689
    pDynCtrl->stbJoin.pVgList = NULL;
18,964✔
6690
    code = nodesMakeList(&pDynCtrl->stbJoin.pVgList);
18,964✔
6691
  }
6692

6693
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6694
    pDynCtrl->stbJoin.pUidList = NULL;
18,964✔
6695
    code = nodesMakeList(&pDynCtrl->stbJoin.pUidList);
18,964✔
6696
  }
6697

6698
  SJoinLogicNode* pHJoin = (SJoinLogicNode*)pPrev;
18,964✔
6699
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6700
    code = nodesListStrictAppend(pDynCtrl->stbJoin.pUidList, nodesListGetNode(pHJoin->node.pTargets, 0));
18,964✔
6701
  }
6702
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6703
    code = nodesListStrictAppend(pDynCtrl->stbJoin.pUidList, nodesListGetNode(pHJoin->node.pTargets, 2));
18,964✔
6704
  }
6705
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6706
    code = nodesListStrictAppend(pDynCtrl->stbJoin.pVgList, nodesListGetNode(pHJoin->node.pTargets, 1));
18,964✔
6707
  }
6708
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6709
    code = nodesListStrictAppend(pDynCtrl->stbJoin.pVgList, nodesListGetNode(pHJoin->node.pTargets, 3));
18,964✔
6710
  }
6711

6712
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6713
    code = nodesListStrictAppend(pDynCtrl->node.pChildren, (SNode*)pPrev);
18,964✔
6714
    if (TSDB_CODE_SUCCESS == code) {
18,964!
6715
      code = nodesListStrictAppend(pDynCtrl->node.pChildren, (SNode*)pPost);
18,964✔
6716
    }
6717
    if (TSDB_CODE_SUCCESS == code) {
18,964!
6718
      pDynCtrl->node.pTargets = NULL;
18,964✔
6719
      code = nodesCloneList(pPost->pTargets, &pDynCtrl->node.pTargets);
18,964✔
6720
    }
6721
  }
6722

6723
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6724
    pPrev->pParent = (SLogicNode*)pDynCtrl;
18,964✔
6725
    pPost->pParent = (SLogicNode*)pDynCtrl;
18,964✔
6726

6727
    *ppDynNode = (SLogicNode*)pDynCtrl;
18,964✔
6728
  } else {
6729
    nodesDestroyNode((SNode*)pDynCtrl);
×
6730
    *ppDynNode = NULL;
×
6731
  }
6732

6733
  return code;
18,964✔
6734
}
6735

6736
static int32_t stbJoinOptRewriteStableJoin(SOptimizeContext* pCxt, SLogicNode* pJoin, SLogicSubplan* pLogicSubplan) {
18,964✔
6737
  SNodeList*  pTagScanNodes = NULL;
18,964✔
6738
  SNodeList*  pTbScanNodes = NULL;
18,964✔
6739
  SLogicNode* pGrpCacheNode = NULL;
18,964✔
6740
  SLogicNode* pHJoinNode = NULL;
18,964✔
6741
  SLogicNode* pMJoinNode = NULL;
18,964✔
6742
  SLogicNode* pDynNode = NULL;
18,964✔
6743
  bool        srcScan[2] = {0};
18,964✔
6744

6745
  int32_t code = stbJoinOptCreateTagScanNode(pJoin, &pTagScanNodes);
18,964✔
6746
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6747
    code = stbJoinOptCreateTagHashJoinNode(pJoin, pTagScanNodes, &pHJoinNode);
18,964✔
6748
  }
6749
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6750
    code = stbJoinOptCreateTableScanNodes(pJoin, &pTbScanNodes, srcScan);
18,964✔
6751
  }
6752
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6753
    code = stbJoinOptCreateGroupCacheNode(getLogicNodeRootNode(pJoin), pTbScanNodes, &pGrpCacheNode);
18,964✔
6754
  }
6755
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6756
    code = stbJoinOptCreateMergeJoinNode(pJoin, pGrpCacheNode, &pMJoinNode);
18,964✔
6757
  }
6758
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6759
    code = stbJoinOptCreateDynQueryCtrlNode(getLogicNodeRootNode(pJoin), pHJoinNode, pMJoinNode, srcScan, &pDynNode);
18,964✔
6760
  }
6761
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6762
    code = replaceLogicNode(pLogicSubplan, pJoin, (SLogicNode*)pDynNode);
18,964✔
6763
  }
6764
  if (TSDB_CODE_SUCCESS == code) {
18,964!
6765
    nodesDestroyNode((SNode*)pJoin);
18,964✔
6766
    pCxt->optimized = true;
18,964✔
6767
  }
6768
  return code;
18,964✔
6769
}
6770

6771
static int32_t stableJoinOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
2,012,738✔
6772
  SLogicNode* pNode = optFindPossibleNode(pLogicSubplan->pNode, stbJoinOptShouldBeOptimized, NULL);
2,012,738✔
6773
  if (NULL == pNode) {
2,012,843✔
6774
    return TSDB_CODE_SUCCESS;
1,993,882✔
6775
  }
6776

6777
  return stbJoinOptRewriteStableJoin(pCxt, pNode, pLogicSubplan);
18,961✔
6778
}
6779

6780
static bool grpJoinOptShouldBeOptimized(SLogicNode* pNode, void* pCtx) {
8,565,131✔
6781
  if (QUERY_NODE_LOGIC_PLAN_JOIN != nodeType(pNode)) {
8,565,131✔
6782
    return false;
8,274,500✔
6783
  }
6784

6785
  SJoinLogicNode* pJoin = (SJoinLogicNode*)pNode;
290,631✔
6786
  if (JOIN_STYPE_ASOF != pJoin->subType && JOIN_STYPE_WIN != pJoin->subType) {
290,631✔
6787
    return false;
287,490✔
6788
  }
6789

6790
  if (NULL == pJoin->pLeftEqNodes || pJoin->grpJoin) {
3,141✔
6791
    return false;
2,616✔
6792
  }
6793

6794
  return true;
525✔
6795
}
6796

6797
static int32_t grpJoinOptCreatePartitionNode(SLogicNode* pParent, SLogicNode* pChild, bool leftChild,
1,050✔
6798
                                             SLogicNode** pNew) {
6799
  SPartitionLogicNode* pPartition = NULL;
1,050✔
6800
  int32_t              code = nodesMakeNode(QUERY_NODE_LOGIC_PLAN_PARTITION, (SNode**)&pPartition);
1,050✔
6801
  if (NULL == pPartition) {
1,050!
6802
    return code;
×
6803
  }
6804

6805
  pPartition->node.groupAction = GROUP_ACTION_SET;
1,050✔
6806
  pPartition->node.requireDataOrder = DATA_ORDER_LEVEL_GLOBAL;
1,050✔
6807
  pPartition->node.resultDataOrder = DATA_ORDER_LEVEL_IN_GROUP;
1,050✔
6808

6809
  pPartition->node.pTargets = NULL;
1,050✔
6810
  code = nodesCloneList(pChild->pTargets, &pPartition->node.pTargets);
1,050✔
6811
  if (NULL == pPartition->node.pTargets) {
1,050!
6812
    nodesDestroyNode((SNode*)pPartition);
×
6813
    return code;
×
6814
  }
6815

6816
  SJoinLogicNode* pJoin = (SJoinLogicNode*)pParent;
1,050✔
6817
  pPartition->pPartitionKeys = NULL;
1,050✔
6818
  code = nodesCloneList(leftChild ? pJoin->pLeftEqNodes : pJoin->pRightEqNodes, &pPartition->pPartitionKeys);
1,050✔
6819
  if (TSDB_CODE_SUCCESS != code) {
1,050!
6820
    nodesDestroyNode((SNode*)pPartition);
×
6821
    return code;
×
6822
  }
6823
  code = nodesListMakeStrictAppend(&pPartition->node.pChildren, (SNode*)pChild);
1,050✔
6824
  if (TSDB_CODE_SUCCESS == code) {
1,050!
6825
    *pNew = (SLogicNode*)pPartition;
1,050✔
6826
    pChild->pParent = (SLogicNode*)pPartition;
1,050✔
6827
    pPartition->node.pParent = pParent;
1,050✔
6828
  } else {
6829
    nodesDestroyNode((SNode*)pPartition);
×
6830
  }
6831
  return code;
1,050✔
6832
}
6833

6834
static int32_t grpJoinOptInsertPartitionNode(SLogicNode* pJoin) {
525✔
6835
  int32_t code = TSDB_CODE_SUCCESS;
525✔
6836
  SNode*  pNode = NULL;
525✔
6837
  SNode*  pNew = NULL;
525✔
6838
  bool    leftChild = true;
525✔
6839
  FOREACH(pNode, pJoin->pChildren) {
1,575!
6840
    code = grpJoinOptCreatePartitionNode(pJoin, (SLogicNode*)pNode, leftChild, (SLogicNode**)&pNew);
1,050✔
6841
    if (code) {
1,050!
6842
      break;
×
6843
    }
6844
    REPLACE_NODE(pNew);
1,050✔
6845
    leftChild = false;
1,050✔
6846
  }
6847

6848
  return code;
525✔
6849
}
6850

6851
static int32_t grpJoinOptPartByTags(SLogicNode* pNode) {
×
6852
  int32_t         code = TSDB_CODE_SUCCESS;
×
6853
  SNode*          pChild = NULL;
×
6854
  SNode*          pNew = NULL;
×
6855
  bool            leftChild = true;
×
6856
  SJoinLogicNode* pJoin = (SJoinLogicNode*)pNode;
×
6857
  FOREACH(pChild, pNode->pChildren) {
×
6858
    if (QUERY_NODE_LOGIC_PLAN_SCAN != nodeType(pChild)) {
×
6859
      return TSDB_CODE_PLAN_INTERNAL_ERROR;
×
6860
    }
6861

6862
    SScanLogicNode* pScan = (SScanLogicNode*)pChild;
×
6863
    SNodeList*      pNewList = NULL;
×
6864
    code = nodesCloneList(pJoin->pLeftEqNodes, &pNewList);
×
6865
    if (TSDB_CODE_SUCCESS == code) {
×
6866
      if (leftChild) {
×
6867
        code = nodesListMakeStrictAppendList(&pScan->pGroupTags, pNewList);
×
6868
        leftChild = false;
×
6869
      } else {
6870
        code = nodesListMakeStrictAppendList(&pScan->pGroupTags, pNewList);
×
6871
      }
6872
    }
6873
    if (TSDB_CODE_SUCCESS != code) {
×
6874
      break;
×
6875
    }
6876

6877
    pScan->groupSort = true;
×
6878
    pScan->groupOrderScan = true;
×
6879
  }
6880

6881
  return code;
×
6882
}
6883

6884
static int32_t grpJoinOptRewriteGroupJoin(SOptimizeContext* pCxt, SLogicNode* pNode, SLogicSubplan* pLogicSubplan) {
525✔
6885
  SJoinLogicNode* pJoin = (SJoinLogicNode*)pNode;
525✔
6886
  int32_t         code = (pJoin->allEqTags && !pJoin->hasSubQuery && !pJoin->batchScanHint)
252!
6887
                             ? grpJoinOptPartByTags(pNode)
×
6888
                             : grpJoinOptInsertPartitionNode(pNode);
777✔
6889
  if (TSDB_CODE_SUCCESS == code) {
525!
6890
    pJoin->grpJoin = true;
525✔
6891
    pCxt->optimized = true;
525✔
6892
  }
6893
  return code;
525✔
6894
}
6895

6896
static int32_t groupJoinOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
1,993,798✔
6897
  SLogicNode* pNode = optFindPossibleNode(pLogicSubplan->pNode, grpJoinOptShouldBeOptimized, NULL);
1,993,798✔
6898
  if (NULL == pNode) {
1,993,883✔
6899
    return TSDB_CODE_SUCCESS;
1,993,344✔
6900
  }
6901

6902
  return grpJoinOptRewriteGroupJoin(pCxt, pNode, pLogicSubplan);
539✔
6903
}
6904

6905
static bool partColOptShouldBeOptimized(SLogicNode* pNode, void* pCtx) {
3,189,162✔
6906
  if (QUERY_NODE_LOGIC_PLAN_PARTITION == nodeType(pNode)) {
3,189,162✔
6907
    SPartitionLogicNode* pPartition = (SPartitionLogicNode*)pNode;
21,391✔
6908
    if (keysHasCol(pPartition->pPartitionKeys)) return true;
21,391✔
6909
  }
6910
  return false;
3,181,296✔
6911
}
6912

6913
static int32_t partColOptCreateSort(SPartitionLogicNode* pPartition, SSortLogicNode** ppSort) {
484✔
6914
  SNode*          node;
6915
  int32_t         code = TSDB_CODE_SUCCESS;
484✔
6916
  SSortLogicNode* pSort = NULL;
484✔
6917
  code = nodesMakeNode(QUERY_NODE_LOGIC_PLAN_SORT, (SNode**)&pSort);
484✔
6918
  if (pSort) {
484!
6919
    bool alreadyPartByPKTs = false;
484✔
6920
    pSort->groupSort = false;
484✔
6921
    FOREACH(node, pPartition->pPartitionKeys) {
1,220!
6922
      SOrderByExprNode* pOrder = NULL;
736✔
6923
      code = nodesMakeNode(QUERY_NODE_ORDER_BY_EXPR, (SNode**)&pOrder);
736✔
6924
      if (TSDB_CODE_SUCCESS != code) {
736!
6925
        break;
×
6926
      }
6927
      if (QUERY_NODE_COLUMN == nodeType(node) && ((SColumnNode*)node)->colId == pPartition->pkTsColId &&
736✔
6928
          ((SColumnNode*)node)->tableId == pPartition->pkTsColTbId)
12!
6929
        alreadyPartByPKTs = true;
12✔
6930
      code = nodesListMakeStrictAppend(&pSort->pSortKeys, (SNode*)pOrder);
736✔
6931
      if (TSDB_CODE_SUCCESS == code) {
736!
6932
        pOrder->order = ORDER_ASC;
736✔
6933
        pOrder->pExpr = NULL;
736✔
6934
        pOrder->nullOrder = NULL_ORDER_FIRST;
736✔
6935
        code = nodesCloneNode(node, &pOrder->pExpr);
736✔
6936
      }
6937
      if (TSDB_CODE_SUCCESS != code) {
736!
6938
        break;
×
6939
      }
6940
    }
6941
    if (TSDB_CODE_SUCCESS != code) {
484!
6942
      nodesDestroyNode((SNode*)pSort);
×
6943
      return code;
×
6944
    }
6945

6946
    if (pPartition->needBlockOutputTsOrder && !alreadyPartByPKTs) {
484!
6947
      SOrderByExprNode* pOrder = NULL;
252✔
6948
      code = nodesMakeNode(QUERY_NODE_ORDER_BY_EXPR, (SNode**)&pOrder);
252✔
6949
      if (pOrder) {
252!
6950
        pSort->excludePkCol = true;
252✔
6951
        code = nodesListMakeStrictAppend(&pSort->pSortKeys, (SNode*)pOrder);
252✔
6952
        if (TSDB_CODE_SUCCESS == code) {
252!
6953
          pOrder->order = ORDER_ASC;
252✔
6954
          pOrder->pExpr = 0;
252✔
6955
          FOREACH(node, pPartition->node.pTargets) {
252!
6956
            if (nodeType(node) == QUERY_NODE_COLUMN) {
252!
6957
              SColumnNode* pCol = (SColumnNode*)node;
252✔
6958
              if (pCol->colId == pPartition->pkTsColId && pCol->tableId == pPartition->pkTsColTbId) {
252!
6959
                pOrder->pExpr = NULL;
252✔
6960
                code = nodesCloneNode((SNode*)pCol, &pOrder->pExpr);
252✔
6961
                break;
252✔
6962
              }
6963
            }
6964
          }
6965
        }
6966
      }
6967
    }
6968
  }
6969
  if (code != TSDB_CODE_SUCCESS) {
484!
6970
    nodesDestroyNode((SNode*)pSort);
×
6971
    pSort = NULL;
×
6972
  } else {
6973
    *ppSort = pSort;
484✔
6974
  }
6975
  return code;
484✔
6976
}
6977

6978
static int32_t partitionColsOpt(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
948,796✔
6979
  SNode*               node;
6980
  int32_t              code = TSDB_CODE_SUCCESS;
948,796✔
6981
  SPartitionLogicNode* pNode =
6982
      (SPartitionLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, partColOptShouldBeOptimized, NULL);
948,796✔
6983
  if (NULL == pNode) return TSDB_CODE_SUCCESS;
948,890✔
6984
  SLogicNode* pRootNode = getLogicNodeRootNode((SLogicNode*)pNode);
7,868✔
6985

6986
  if (pRootNode->pHint && getSortForGroupOptHint(pRootNode->pHint)) {
7,866✔
6987
    // replace with sort node
6988
    SSortLogicNode* pSort = NULL;
484✔
6989
    code = partColOptCreateSort(pNode, &pSort);
484✔
6990
    if (!pSort) {
484!
6991
      // if sort create failed, we eat the error, skip the optimization
6992
      code = TSDB_CODE_SUCCESS;
×
6993
    } else {
6994
      TSWAP(pSort->node.pChildren, pNode->node.pChildren);
484✔
6995
      TSWAP(pSort->node.pTargets, pNode->node.pTargets);
484✔
6996
      optResetParent((SLogicNode*)pSort);
484✔
6997
      pSort->calcGroupId = true;
484✔
6998
      code = replaceLogicNode(pLogicSubplan, (SLogicNode*)pNode, (SLogicNode*)pSort);
484✔
6999
      if (code == TSDB_CODE_SUCCESS) {
484!
7000
        nodesDestroyNode((SNode*)pNode);
484✔
7001
        pCxt->optimized = true;
484✔
7002
      } else {
7003
        nodesDestroyNode((SNode*)pSort);
×
7004
      }
7005
    }
7006
    return code;
484✔
7007
  } else if (pNode->node.pParent && nodeType(pNode->node.pParent) == QUERY_NODE_LOGIC_PLAN_AGG &&
7,382✔
7008
             !getOptHint(pRootNode->pHint, HINT_PARTITION_FIRST)) {
2,103✔
7009
    // Check if we can delete partition node
7010
    SAggLogicNode* pAgg = (SAggLogicNode*)pNode->node.pParent;
2,095✔
7011
    FOREACH(node, pNode->pPartitionKeys) {
5,177!
7012
      SGroupingSetNode* pgsNode = NULL;
3,082✔
7013
      code = nodesMakeNode(QUERY_NODE_GROUPING_SET, (SNode**)&pgsNode);
3,082✔
7014
      if (code == TSDB_CODE_SUCCESS) {
3,082!
7015
        pgsNode->groupingSetType = GP_TYPE_NORMAL;
3,082✔
7016
        pgsNode->pParameterList = NULL;
3,082✔
7017
        code = nodesMakeList(&pgsNode->pParameterList);
3,082✔
7018
      }
7019
      if (code == TSDB_CODE_SUCCESS) {
3,082!
7020
        SNode* pNew = NULL;
3,082✔
7021
        code = nodesCloneNode(node, &pNew);
3,082✔
7022
        if (TSDB_CODE_SUCCESS == code) {
3,082!
7023
          code = nodesListStrictAppend(pgsNode->pParameterList, pNew);
3,082✔
7024
        }
7025
      }
7026
      if (code == TSDB_CODE_SUCCESS) {
3,082!
7027
        // Now we are using hash agg
7028
        code = nodesListMakeAppend(&pAgg->pGroupKeys, (SNode*)pgsNode);
3,082✔
7029
      }
7030
      if (code != TSDB_CODE_SUCCESS) {
3,082!
7031
        nodesDestroyNode((SNode*)pgsNode);
×
7032
        break;
×
7033
      }
7034
    }
7035

7036
    if (code == TSDB_CODE_SUCCESS) {
2,095!
7037
      code =
7038
          replaceLogicNode(pLogicSubplan, (SLogicNode*)pNode, (SLogicNode*)nodesListGetNode(pNode->node.pChildren, 0));
2,095✔
7039
      NODES_CLEAR_LIST(pNode->node.pChildren);
2,095✔
7040
    }
7041
    if (code == TSDB_CODE_SUCCESS) {
2,095!
7042
      // For hash agg, nonblocking mode is meaningless, slimit is useless, so we reset it
7043
      pAgg->node.forceCreateNonBlockingOptr = false;
2,095✔
7044
      nodesDestroyNode(pAgg->node.pSlimit);
2,095✔
7045
      pAgg->node.pSlimit = NULL;
2,095✔
7046
      nodesDestroyNode((SNode*)pNode);
2,095✔
7047
      pCxt->optimized = true;
2,095✔
7048
    }
7049
    return code;
2,095✔
7050
  }
7051

7052
  return code;
5,287✔
7053
}
7054

7055
static bool tsmaOptMayBeOptimized(SLogicNode* pNode, void* pCtx) {
3,191,048✔
7056
  if (QUERY_NODE_LOGIC_PLAN_SCAN == nodeType(pNode)) {
3,191,048✔
7057
    SNode*          pTmpNode;
7058
    SNodeList*      pFuncs = NULL;
1,207,719✔
7059
    SScanLogicNode* pScan = (SScanLogicNode*)pNode;
1,207,719✔
7060
    SLogicNode*     pParent = pScan->node.pParent;
1,207,719✔
7061
    SNode*          pConds = pScan->node.pConditions;
1,207,719✔
7062

7063
    if (pScan->scanType != SCAN_TYPE_TABLE || !pParent || pConds) return false;
1,207,719✔
7064
    if (!pScan->pTsmas || pScan->pTsmas->size <= 0) {
710,924!
7065
      return false;
709,398✔
7066
    }
7067

7068
    switch (nodeType(pParent)) {
1,526!
7069
      case QUERY_NODE_LOGIC_PLAN_WINDOW: {
1,263✔
7070
        SWindowLogicNode* pWindow = (SWindowLogicNode*)pParent;
1,263✔
7071
        // only time window interval supported
7072
        if (pWindow->winType != WINDOW_TYPE_INTERVAL) return false;
1,263!
7073
        pFuncs = pWindow->pFuncs;
1,263✔
7074
      } break;
1,263✔
7075
      case QUERY_NODE_LOGIC_PLAN_AGG: {
263✔
7076
        SAggLogicNode* pAgg = (SAggLogicNode*)pParent;
263✔
7077
        // group/partition by normal cols not supported
7078
        if (pAgg->pGroupKeys) return false;
263✔
7079
        pFuncs = pAgg->pAggFuncs;
231✔
7080
      } break;
231✔
7081
      default:
×
7082
        return false;
×
7083
    }
7084

7085
    FOREACH(pTmpNode, pFuncs) {
139,417!
7086
      SFunctionNode* pFunc = (SFunctionNode*)pTmpNode;
137,933✔
7087
      if (!fmIsTSMASupportedFunc(pFunc->funcId) && !fmIsPseudoColumnFunc(pFunc->funcId) &&
137,933✔
7088
          !fmIsGroupKeyFunc(pFunc->funcId)) {
720✔
7089
        return false;
10✔
7090
      }
7091
    }
7092

7093
    if (nodeType(pParent) == QUERY_NODE_LOGIC_PLAN_WINDOW) {
1,484✔
7094
      SWindowLogicNode* pWindow = (SWindowLogicNode*)pParent;
1,263✔
7095
      if (pWindow->winType == WINDOW_TYPE_INTERVAL && pWindow->offset == AUTO_DURATION_VALUE) {
1,263!
7096
        SLogicNode* pRootNode = getLogicNodeRootNode(pParent);
×
7097
        // When using interval auto offset, tsma optimization cannot take effect.
7098
        // Unless the user explicitly specifies not to use tsma, an error should be reported.
7099
        if (!getOptHint(pRootNode->pHint, HINT_SKIP_TSMA)) {
×
7100
          planError("%s failed since tsma optimization cannot be applied with interval auto offset", __func__);
×
7101
          *(int32_t*)pCtx = TSDB_CODE_TSMA_INVALID_AUTO_OFFSET;
×
7102
          return false;
×
7103
        }
7104
      }
7105
    }
7106

7107
    return true;
1,484✔
7108
  }
7109
  return false;
1,983,329✔
7110
}
7111

7112
typedef struct STSMAOptUsefulTsma {
7113
  const STableTSMAInfo* pTsma;          // NULL if no tsma available, which will use original data for calculation
7114
  STimeWindow           scanRange;      // scan time range for this tsma
7115
  SArray*               pTsmaScanCols;  // SArray<int32_t> index of tsmaFuncs array
7116
  char                  targetTbName[TSDB_TABLE_NAME_LEN];  // the scanning table name, used only when pTsma is not NULL
7117
  uint64_t              targetTbUid;                        // the scanning table uid, used only when pTsma is not NULL
7118
  int8_t                precision;
7119
} STSMAOptUsefulTsma;
7120

7121
typedef struct STSMAOptCtx {
7122
  // input
7123
  SScanLogicNode*    pScan;
7124
  SLogicNode*        pParent;  // parent of Table Scan, Agg or Interval
7125
  const SNodeList*   pAggFuncs;
7126
  const STimeWindow* pTimeRange;
7127
  const SArray*      pTsmas;
7128
  SInterval*         queryInterval;  // not null with window logic node
7129
  int8_t             precision;
7130

7131
  // output
7132
  SArray*        pUsefulTsmas;  // SArray<STSMAOptUseFulTsma>, sorted by tsma interval from long to short
7133
  SArray*        pUsedTsmas;
7134
  SLogicSubplan* generatedSubPlans[2];
7135
  SNodeList**    ppParentTsmaSubplans;
7136
} STSMAOptCtx;
7137

7138
static int32_t fillTSMAOptCtx(STSMAOptCtx* pTsmaOptCtx, SScanLogicNode* pScan, void* tz) {
1,484✔
7139
  int32_t code = 0;
1,484✔
7140
  pTsmaOptCtx->pScan = pScan;
1,484✔
7141
  pTsmaOptCtx->pParent = pScan->node.pParent;
1,484✔
7142
  pTsmaOptCtx->pTsmas = pScan->pTsmas;
1,484✔
7143
  pTsmaOptCtx->pTimeRange = &pScan->scanRange;
1,484✔
7144
  pTsmaOptCtx->precision = pScan->node.precision;
1,484✔
7145

7146
  if (nodeType(pTsmaOptCtx->pParent) == QUERY_NODE_LOGIC_PLAN_WINDOW) {
1,484✔
7147
    pTsmaOptCtx->queryInterval = taosMemoryCalloc(1, sizeof(SInterval));
1,263!
7148
    if (!pTsmaOptCtx->queryInterval) return terrno;
1,263!
7149

7150
    SWindowLogicNode* pWindow = (SWindowLogicNode*)pTsmaOptCtx->pParent;
1,263✔
7151
    pTsmaOptCtx->queryInterval->interval = pWindow->interval;
1,263✔
7152
    pTsmaOptCtx->queryInterval->intervalUnit = pWindow->intervalUnit;
1,263✔
7153
    pTsmaOptCtx->queryInterval->offset = pWindow->offset;
1,263✔
7154
    pTsmaOptCtx->queryInterval->offsetUnit = pWindow->intervalUnit;
1,263✔
7155
    pTsmaOptCtx->queryInterval->sliding = pWindow->sliding;
1,263✔
7156
    pTsmaOptCtx->queryInterval->slidingUnit = pWindow->slidingUnit;
1,263✔
7157
    pTsmaOptCtx->queryInterval->precision = pWindow->node.precision;
1,263✔
7158
    pTsmaOptCtx->queryInterval->timezone = tz;
1,263✔
7159
    pTsmaOptCtx->pAggFuncs = pWindow->pFuncs;
1,263✔
7160
    pTsmaOptCtx->ppParentTsmaSubplans = &pWindow->pTsmaSubplans;
1,263✔
7161
  } else {
7162
    SAggLogicNode* pAgg = (SAggLogicNode*)pTsmaOptCtx->pParent;
221✔
7163
    pTsmaOptCtx->pAggFuncs = pAgg->pAggFuncs;
221✔
7164
    pTsmaOptCtx->ppParentTsmaSubplans = &pAgg->pTsmaSubplans;
221✔
7165
  }
7166
  pTsmaOptCtx->pUsefulTsmas = taosArrayInit(pScan->pTsmas->size, sizeof(STSMAOptUsefulTsma));
1,484✔
7167
  pTsmaOptCtx->pUsedTsmas = taosArrayInit(3, sizeof(STSMAOptUsefulTsma));
1,484✔
7168
  if (!pTsmaOptCtx->pUsefulTsmas || !pTsmaOptCtx->pUsedTsmas) {
1,484!
7169
    code = terrno;
×
7170
  }
7171
  return code;
1,484✔
7172
}
7173

7174
static void tsmaOptFreeUsefulTsma(void* p) {
1,744✔
7175
  STSMAOptUsefulTsma* pTsma = p;
1,744✔
7176
  taosArrayDestroy(pTsma->pTsmaScanCols);
1,744✔
7177
}
1,744✔
7178

7179
static void clearTSMAOptCtx(STSMAOptCtx* pTsmaOptCtx) {
1,484✔
7180
  taosArrayDestroyEx(pTsmaOptCtx->pUsefulTsmas, tsmaOptFreeUsefulTsma);
1,484✔
7181
  taosArrayDestroy(pTsmaOptCtx->pUsedTsmas);
1,484✔
7182
  taosMemoryFreeClear(pTsmaOptCtx->queryInterval);
1,484!
7183
}
1,484✔
7184

7185
static bool tsmaOptCheckValidInterval(int64_t tsmaInterval, int8_t unit, const STSMAOptCtx* pTsmaOptCtx) {
2,243✔
7186
  if (!pTsmaOptCtx->queryInterval) return true;
2,243✔
7187

7188
  bool validInterval = checkRecursiveTsmaInterval(tsmaInterval, unit, pTsmaOptCtx->queryInterval->interval,
1,584✔
7189
                                                  pTsmaOptCtx->queryInterval->intervalUnit,
1,584✔
7190
                                                  pTsmaOptCtx->queryInterval->precision, false);
1,584✔
7191
  bool validSliding =
7192
      checkRecursiveTsmaInterval(tsmaInterval, unit, pTsmaOptCtx->queryInterval->sliding,
1,584✔
7193
                                 pTsmaOptCtx->queryInterval->slidingUnit, pTsmaOptCtx->queryInterval->precision, false);
1,584✔
7194
  bool validOffset =
1,584✔
7195
      pTsmaOptCtx->queryInterval->offset == 0 ||
1,584!
7196
      checkRecursiveTsmaInterval(tsmaInterval, unit, pTsmaOptCtx->queryInterval->offset,
×
7197
                                 pTsmaOptCtx->queryInterval->offsetUnit, pTsmaOptCtx->queryInterval->precision, false);
×
7198
  return validInterval && validSliding && validOffset;
1,584!
7199
}
7200

7201
static int32_t tsmaOptCheckValidFuncs(const SArray* pTsmaFuncs, const SNodeList* pQueryFuncs, SArray* pTsmaScanCols,
1,813✔
7202
                                      bool* pIsValid) {
7203
  SNode* pNode;
7204
  bool   failed = false, found = false;
1,813✔
7205

7206
  taosArrayClear(pTsmaScanCols);
1,813✔
7207
  FOREACH(pNode, pQueryFuncs) {
45,375!
7208
    SFunctionNode* pQueryFunc = (SFunctionNode*)pNode;
43,631✔
7209
    if (fmIsPseudoColumnFunc(pQueryFunc->funcId) || fmIsGroupKeyFunc(pQueryFunc->funcId)) continue;
43,631✔
7210
    if (nodeType(pQueryFunc->pParameterList->pHead->pNode) != QUERY_NODE_COLUMN) {
41,168!
7211
      failed = true;
×
7212
      break;
×
7213
    }
7214
    int32_t queryColId = ((SColumnNode*)pQueryFunc->pParameterList->pHead->pNode)->colId;
41,168✔
7215
    found = false;
41,168✔
7216
    int32_t notMyStateFuncId = -1;
41,168✔
7217
    // iterate funcs
7218
    for (int32_t i = 0; i < pTsmaFuncs->size; i++) {
66,307,019✔
7219
      STableTSMAFuncInfo* pTsmaFuncInfo = taosArrayGet(pTsmaFuncs, i);
66,306,950✔
7220
      if (pTsmaFuncInfo->funcId == notMyStateFuncId) continue;
66,306,950✔
7221

7222
      if (!fmIsMyStateFunc(pQueryFunc->funcId, pTsmaFuncInfo->funcId)) {
16,664,066✔
7223
        notMyStateFuncId = pTsmaFuncInfo->funcId;
61,272✔
7224
        continue;
61,272✔
7225
      }
7226

7227
      if (queryColId != pTsmaFuncInfo->colId) {
16,602,794✔
7228
        continue;
16,561,695✔
7229
      }
7230
      found = true;
41,099✔
7231
      if (NULL == taosArrayPush(pTsmaScanCols, &i)) {
41,099!
7232
        return terrno;
×
7233
      }
7234
      break;
41,099✔
7235
    }
7236
    if (failed || !found) {
41,168!
7237
      break;
7238
    }
7239
  }
7240
  *pIsValid = found;
1,813✔
7241
  return TSDB_CODE_SUCCESS;
1,813✔
7242
}
7243

7244
typedef struct STsmaOptTagCheckCtx {
7245
  const STableTSMAInfo* pTsma;
7246
  bool                  ok;
7247
} STsmaOptTagCheckCtx;
7248

7249
static EDealRes tsmaOptTagCheck(SNode* pNode, void* pContext) {
7,504✔
7250
  bool found = false;
7,504✔
7251
  if (nodeType(pNode) == QUERY_NODE_COLUMN) {
7,504✔
7252
    SColumnNode* pCol = (SColumnNode*)pNode;
3,208✔
7253
    if (pCol->colType == COLUMN_TYPE_TAG) {
3,208!
7254
      STsmaOptTagCheckCtx* pCtx = pContext;
3,208✔
7255
      for (int32_t i = 0; i < pCtx->pTsma->pTags->size; ++i) {
25,664✔
7256
        SSchema* pSchema = taosArrayGet(pCtx->pTsma->pTags, i);
22,456✔
7257
        if (strcmp(pSchema->name, pCol->colName) == 0) {
22,456✔
7258
          found = true;
3,208✔
7259
        }
7260
      }
7261
      if (!found) {
3,208!
7262
        pCtx->ok = false;
×
7263
        return DEAL_RES_END;
×
7264
      }
7265
    }
7266
  }
7267
  return DEAL_RES_CONTINUE;
7,504✔
7268
}
7269

7270
static bool tsmaOptCheckTags(STSMAOptCtx* pCtx, const STableTSMAInfo* pTsma) {
1,744✔
7271
  const SScanLogicNode* pScan = pCtx->pScan;
1,744✔
7272
  STsmaOptTagCheckCtx   ctx = {.pTsma = pTsma, .ok = true};
1,744✔
7273
  nodesWalkExpr(pScan->pTagCond, tsmaOptTagCheck, &ctx);
1,744✔
7274
  if (!ctx.ok) return false;
1,744!
7275
  nodesWalkExprs(pScan->pScanPseudoCols, tsmaOptTagCheck, &ctx);
1,744✔
7276
  if (!ctx.ok) return false;
1,744!
7277
  nodesWalkExprs(pScan->pGroupTags, tsmaOptTagCheck, &ctx);
1,744✔
7278
  return ctx.ok;
1,744✔
7279
}
7280

7281
static int32_t tsmaOptFilterTsmas(STSMAOptCtx* pTsmaOptCtx) {
1,484✔
7282
  STSMAOptUsefulTsma usefulTsma = {
1,484✔
7283
      .pTsma = NULL, .scanRange = {.skey = TSKEY_MIN, .ekey = TSKEY_MAX}, .precision = pTsmaOptCtx->precision};
1,484✔
7284
  SArray* pTsmaScanCols = NULL;
1,484✔
7285
  int32_t code = 0;
1,484✔
7286

7287
  for (int32_t i = 0; i < pTsmaOptCtx->pTsmas->size; ++i) {
4,515✔
7288
    if (!pTsmaScanCols) {
3,031✔
7289
      pTsmaScanCols = taosArrayInit(pTsmaOptCtx->pAggFuncs->length, sizeof(int32_t));
2,619✔
7290
      if (!pTsmaScanCols) return terrno;
2,619!
7291
    }
7292
    if (pTsmaOptCtx->pScan->tableType == TSDB_CHILD_TABLE || pTsmaOptCtx->pScan->tableType == TSDB_NORMAL_TABLE) {
3,031✔
7293
      const STsmaTargetTbInfo* ptbInfo = taosArrayGet(pTsmaOptCtx->pScan->pTsmaTargetTbInfo, i);
713✔
7294
      if (ptbInfo->uid == 0)
713✔
7295
        continue;  // tsma res table meta not found, skip this tsma, this is possible when there is no data in this ctb
1,287✔
7296
    }
7297

7298
    STableTSMAInfo* pTsma = taosArrayGetP(pTsmaOptCtx->pTsmas, i);
3,027✔
7299
    if (!pTsma->fillHistoryFinished ||
3,027✔
7300
        tsMaxTsmaCalcDelay * 1000 < (pTsma->rspTs - pTsma->reqTs) + pTsma->delayDuration) {
2,243!
7301
      continue;
784✔
7302
    }
7303
    // filter with interval
7304
    if (!tsmaOptCheckValidInterval(pTsma->interval, pTsma->unit, pTsmaOptCtx)) {
2,243✔
7305
      continue;
430✔
7306
    }
7307
    // filter with funcs, note that tsma funcs has been sorted by funcId and ColId
7308
    bool    valid = false;
1,813✔
7309
    int32_t code = tsmaOptCheckValidFuncs(pTsma->pFuncs, pTsmaOptCtx->pAggFuncs, pTsmaScanCols, &valid);
1,813✔
7310
    if (TSDB_CODE_SUCCESS != code) break;
1,813!
7311
    if (!valid) continue;
1,813✔
7312

7313
    if (!tsmaOptCheckTags(pTsmaOptCtx, pTsma)) continue;
1,744!
7314
    usefulTsma.pTsma = pTsma;
1,744✔
7315
    usefulTsma.pTsmaScanCols = pTsmaScanCols;
1,744✔
7316
    pTsmaScanCols = NULL;
1,744✔
7317
    if (NULL == taosArrayPush(pTsmaOptCtx->pUsefulTsmas, &usefulTsma)) {
3,488!
7318
      if (pTsmaScanCols) {
×
7319
        taosArrayDestroy(pTsmaScanCols);
×
7320
      }
7321
      return terrno;
×
7322
    }
7323
  }
7324
  if (pTsmaScanCols) taosArrayDestroy(pTsmaScanCols);
1,484✔
7325
  return code;
1,484✔
7326
}
7327

7328
static int32_t tsmaInfoCompWithIntervalDesc(const void* pLeft, const void* pRight) {
1,107✔
7329
  const int64_t             factors[3] = {NANOSECOND_PER_MSEC, NANOSECOND_PER_USEC, 1};
1,107✔
7330
  const STSMAOptUsefulTsma *p = pLeft, *q = pRight;
1,107✔
7331
  int64_t                   pInterval = p->pTsma->interval, qInterval = q->pTsma->interval;
1,107✔
7332
  int8_t                    pUnit = p->pTsma->unit, qUnit = q->pTsma->unit;
1,107✔
7333
  if (TIME_UNIT_MONTH == pUnit) {
1,107!
7334
    pInterval = pInterval * 31 * (NANOSECOND_PER_DAY / factors[p->precision]);
×
7335
  } else if (TIME_UNIT_YEAR == pUnit) {
1,107✔
7336
    pInterval = pInterval * 365 * (NANOSECOND_PER_DAY / factors[p->precision]);
309✔
7337
  }
7338
  if (TIME_UNIT_MONTH == qUnit) {
1,107✔
7339
    qInterval = qInterval * 31 * (NANOSECOND_PER_DAY / factors[q->precision]);
726✔
7340
  } else if (TIME_UNIT_YEAR == qUnit) {
381✔
7341
    qInterval = qInterval * 365 * (NANOSECOND_PER_DAY / factors[q->precision]);
313✔
7342
  }
7343

7344
  if (pInterval > qInterval) return -1;
1,107✔
7345
  if (pInterval < qInterval) return 1;
786!
7346
  return 0;
×
7347
}
7348

7349
static void tsmaOptInitIntervalFromTsma(SInterval* pInterval, const STableTSMAInfo* pTsma, int8_t precision, void* tz) {
1,633✔
7350
  pInterval->interval = pTsma->interval;
1,633✔
7351
  pInterval->intervalUnit = pTsma->unit;
1,633✔
7352
  pInterval->sliding = pTsma->interval;
1,633✔
7353
  pInterval->slidingUnit = pTsma->unit;
1,633✔
7354
  pInterval->offset = 0;
1,633✔
7355
  pInterval->offsetUnit = pTsma->unit;
1,633✔
7356
  pInterval->precision = precision;
1,633✔
7357
  pInterval->timezone = tz;
1,633✔
7358
}
1,633✔
7359

7360
static const STSMAOptUsefulTsma* tsmaOptFindUsefulTsma(const SArray* pUsefulTsmas, int32_t startIdx,
378✔
7361
                                                       int64_t startAlignInterval, int64_t endAlignInterval,
7362
                                                       int8_t precision, void* tz) {
7363
  SInterval tsmaInterval;
7364
  for (int32_t i = startIdx; i < pUsefulTsmas->size; ++i) {
817✔
7365
    const STSMAOptUsefulTsma* pUsefulTsma = taosArrayGet(pUsefulTsmas, i);
495✔
7366
    tsmaOptInitIntervalFromTsma(&tsmaInterval, pUsefulTsma->pTsma, precision, tz);
495✔
7367
    if (taosTimeTruncate(startAlignInterval, &tsmaInterval) == startAlignInterval &&
613✔
7368
        taosTimeTruncate(endAlignInterval, &tsmaInterval) == endAlignInterval) {
118✔
7369
      return pUsefulTsma;
56✔
7370
    }
7371
  }
7372
  return NULL;
322✔
7373
}
7374

7375
static int32_t tsmaOptSplitWindows(STSMAOptCtx* pTsmaOptCtx, const STimeWindow* pScanRange, void* tz) {
950✔
7376
  bool                      needTailWindow = false;
950✔
7377
  bool                      isSkeyAlignedWithTsma = true, isEkeyAlignedWithTsma = true;
950✔
7378
  int32_t                   code = 0;
950✔
7379
  int64_t                   winSkey = TSKEY_MIN, winEkey = TSKEY_MAX;
950✔
7380
  int64_t                   startOfSkeyFirstWin = pScanRange->skey, endOfSkeyFirstWin;
950✔
7381
  int64_t                   startOfEkeyFirstWin = pScanRange->ekey, endOfEkeyFirstWin;
950✔
7382
  SInterval                 interval, tsmaInterval;
7383
  STimeWindow               scanRange = *pScanRange;
950✔
7384
  const SInterval*          pInterval = pTsmaOptCtx->queryInterval;
950✔
7385
  const STSMAOptUsefulTsma* pUsefulTsma = taosArrayGet(pTsmaOptCtx->pUsefulTsmas, 0);
950✔
7386
  const STableTSMAInfo*     pTsma = pUsefulTsma->pTsma;
950✔
7387

7388
  if (pScanRange->ekey <= pScanRange->skey) return code;
950✔
7389

7390
  if (!pInterval) {
925✔
7391
    tsmaOptInitIntervalFromTsma(&interval, pTsma, pTsmaOptCtx->precision, tz);
213✔
7392
    pInterval = &interval;
213✔
7393
  }
7394

7395
  tsmaOptInitIntervalFromTsma(&tsmaInterval, pTsma, pTsmaOptCtx->precision, tz);
925✔
7396

7397
  // check for head windows
7398
  if (pScanRange->skey != TSKEY_MIN) {
925✔
7399
    startOfSkeyFirstWin = taosTimeTruncate(pScanRange->skey, pInterval);
285✔
7400
    endOfSkeyFirstWin =
7401
        taosTimeAdd(startOfSkeyFirstWin, pInterval->interval, pInterval->intervalUnit, pTsmaOptCtx->precision, tz);
285✔
7402
    isSkeyAlignedWithTsma = taosTimeTruncate(pScanRange->skey, &tsmaInterval) == pScanRange->skey;
285✔
7403
  } else {
7404
    endOfSkeyFirstWin = TSKEY_MIN;
640✔
7405
  }
7406

7407
  // check for tail windows
7408
  if (pScanRange->ekey != TSKEY_MAX) {
925✔
7409
    startOfEkeyFirstWin = taosTimeTruncate(pScanRange->ekey, pInterval);
217✔
7410
    endOfEkeyFirstWin =
7411
        taosTimeAdd(startOfEkeyFirstWin, pInterval->interval, pInterval->intervalUnit, pTsmaOptCtx->precision, tz);
217✔
7412
    isEkeyAlignedWithTsma = taosTimeTruncate(pScanRange->ekey + 1, &tsmaInterval) == (pScanRange->ekey + 1);
217✔
7413
    if (startOfEkeyFirstWin > startOfSkeyFirstWin) {
217✔
7414
      needTailWindow = true;
106✔
7415
    }
7416
  }
7417

7418
  // add head tsma if possible
7419
  if (!isSkeyAlignedWithTsma) {
925✔
7420
    scanRange.ekey = TMIN(
276✔
7421
        scanRange.ekey,
7422
        taosTimeAdd(startOfSkeyFirstWin, pInterval->interval * 1, pInterval->intervalUnit, pTsmaOptCtx->precision, tz) - 1);
7423
    const STSMAOptUsefulTsma* pTsmaFound =
7424
        tsmaOptFindUsefulTsma(pTsmaOptCtx->pUsefulTsmas, 1, scanRange.skey, scanRange.ekey + 1, pTsmaOptCtx->precision, tz);
276✔
7425
    STSMAOptUsefulTsma usefulTsma = {.pTsma = pTsmaFound ? pTsmaFound->pTsma : NULL,
276✔
7426
                                     .scanRange = scanRange,
7427
                                     .pTsmaScanCols = pTsmaFound ? pTsmaFound->pTsmaScanCols : NULL};
276✔
7428
    if (NULL == taosArrayPush(pTsmaOptCtx->pUsedTsmas, &usefulTsma)) return terrno;
552!
7429
  }
7430

7431
  // the main tsma
7432
  if (endOfSkeyFirstWin < startOfEkeyFirstWin ||
925✔
7433
      (endOfSkeyFirstWin == startOfEkeyFirstWin && (isSkeyAlignedWithTsma || isEkeyAlignedWithTsma))) {
6!
7434
    scanRange.ekey = TMIN(pScanRange->ekey, isEkeyAlignedWithTsma ? pScanRange->ekey : startOfEkeyFirstWin - 1);
808✔
7435
    if (!isSkeyAlignedWithTsma) {
808✔
7436
      scanRange.skey = endOfSkeyFirstWin;
164✔
7437
    }
7438
    STSMAOptUsefulTsma usefulTsma = {
808✔
7439
        .pTsma = pTsma, .scanRange = scanRange, .pTsmaScanCols = pUsefulTsma->pTsmaScanCols};
808✔
7440
    if (NULL == taosArrayPush(pTsmaOptCtx->pUsedTsmas, &usefulTsma)) return terrno;
1,616!
7441
  }
7442

7443
  // add tail tsma if possible
7444
  if (!isEkeyAlignedWithTsma && needTailWindow) {
925✔
7445
    scanRange.skey = startOfEkeyFirstWin;
102✔
7446
    scanRange.ekey = pScanRange->ekey;
102✔
7447
    const STSMAOptUsefulTsma* pTsmaFound =
7448
        tsmaOptFindUsefulTsma(pTsmaOptCtx->pUsefulTsmas, 1, scanRange.skey - startOfEkeyFirstWin,
102✔
7449
                              scanRange.ekey + 1 - startOfEkeyFirstWin, pTsmaOptCtx->precision, tz);
102✔
7450
    STSMAOptUsefulTsma usefulTsma = {.pTsma = pTsmaFound ? pTsmaFound->pTsma : NULL,
102✔
7451
                                     .scanRange = scanRange,
7452
                                     .pTsmaScanCols = pTsmaFound ? pTsmaFound->pTsmaScanCols : NULL};
102✔
7453
    if (NULL == taosArrayPush(pTsmaOptCtx->pUsedTsmas, &usefulTsma)) return terrno;
204!
7454
  }
7455
  return code;
925✔
7456
}
7457

7458
int32_t tsmaOptCreateTsmaScanCols(const STSMAOptUsefulTsma* pTsma, const SNodeList* pAggFuncs, SNodeList** ppList) {
864✔
7459
  if (!pTsma->pTsma || !pTsma->pTsmaScanCols) {
864!
7460
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
×
7461
  }
7462
  int32_t    code;
7463
  SNode*     pNode;
7464
  SNodeList* pScanCols = NULL;
864✔
7465

7466
  int32_t i = 0;
864✔
7467

7468
  FOREACH(pNode, pAggFuncs) {
38,221!
7469
    SFunctionNode* pFunc = (SFunctionNode*)pNode;
37,357✔
7470
    if (fmIsPseudoColumnFunc(pFunc->funcId) || fmIsGroupKeyFunc(pFunc->funcId)) {
37,357✔
7471
      continue;
898✔
7472
    }
7473
    const int32_t* idx = taosArrayGet(pTsma->pTsmaScanCols, i);
36,459✔
7474
    SColumnNode*   pCol = NULL;
36,459✔
7475
    code = nodesMakeNode(QUERY_NODE_COLUMN, (SNode**)&pCol);
36,459✔
7476
    if (pCol) {
36,459!
7477
      pCol->colId = *idx + 2;
36,459✔
7478
      pCol->tableType = TSDB_SUPER_TABLE;
36,459✔
7479
      pCol->tableId = pTsma->targetTbUid;
36,459✔
7480
      pCol->colType = COLUMN_TYPE_COLUMN;
36,459✔
7481
      tstrncpy(pCol->tableName, pTsma->targetTbName, TSDB_TABLE_NAME_LEN);
36,459✔
7482
      tstrncpy(pCol->colName, pFunc->node.aliasName, TSDB_COL_NAME_LEN);
36,459✔
7483
      tstrncpy(pCol->node.aliasName, pFunc->node.aliasName, TSDB_COL_NAME_LEN);
36,459✔
7484
      pCol->node.resType.type = TSDB_DATA_TYPE_BINARY;
36,459✔
7485
      code = nodesListMakeStrictAppend(&pScanCols, (SNode*)pCol);
36,459✔
7486
    }
7487
    if (code) break;
36,459!
7488
    ++i;
36,459✔
7489
  }
7490

7491
  if (code) {
864!
7492
    nodesDestroyList(pScanCols);
×
7493
    pScanCols = NULL;
×
7494
  } else {
7495
    *ppList = pScanCols;
864✔
7496
  }
7497
  return code;
864✔
7498
}
7499

7500
static int32_t tsmaOptRewriteTag(const STSMAOptCtx* pTsmaOptCtx, const STSMAOptUsefulTsma* pTsma,
1,512✔
7501
                                 SColumnNode* pTagCol) {
7502
  bool found = false;
1,512✔
7503
  if (pTagCol->colType != COLUMN_TYPE_TAG) return 0;
1,512!
7504
  for (int32_t i = 0; i < pTsma->pTsma->pTags->size; ++i) {
5,448!
7505
    const SSchema* pSchema = taosArrayGet(pTsma->pTsma->pTags, i);
5,448✔
7506
    if (strcmp(pTagCol->colName, pSchema->name) == 0) {
5,448✔
7507
      tstrncpy(pTagCol->tableName, pTsma->targetTbName, TSDB_TABLE_NAME_LEN);
1,512✔
7508
      tstrncpy(pTagCol->tableAlias, pTsma->targetTbName, TSDB_TABLE_NAME_LEN);
1,512✔
7509
      pTagCol->tableId = pTsma->targetTbUid;
1,512✔
7510
      pTagCol->tableType = TSDB_SUPER_TABLE;
1,512✔
7511
      pTagCol->colId = pSchema->colId;
1,512✔
7512
      found = true;
1,512✔
7513
      break;
1,512✔
7514
    }
7515
  }
7516
  return found ? TSDB_CODE_SUCCESS : TSDB_CODE_PLAN_INTERNAL_ERROR;
1,512!
7517
}
7518

7519
static int32_t tsmaOptRewriteTbname(const STSMAOptCtx* pTsmaOptCtx, SNode** pTbNameNode,
804✔
7520
                                    const STSMAOptUsefulTsma* pTsma) {
7521
  int32_t    code = 0;
804✔
7522
  SExprNode* pRewrittenFunc = NULL;
804✔
7523
  code = nodesMakeNode(pTsma ? QUERY_NODE_COLUMN : QUERY_NODE_FUNCTION, (SNode**)&pRewrittenFunc);
804✔
7524
  SValueNode* pValue = NULL;
804✔
7525
  if (code == TSDB_CODE_SUCCESS) {
804!
7526
    pRewrittenFunc->resType = ((SExprNode*)(*pTbNameNode))->resType;
804✔
7527
  }
7528

7529
  if (pTsma && code == TSDB_CODE_SUCCESS) {
804!
7530
    nodesDestroyNode(*pTbNameNode);
690✔
7531
    SColumnNode*   pCol = (SColumnNode*)pRewrittenFunc;
690✔
7532
    const SSchema* pSchema = taosArrayGet(pTsma->pTsma->pTags, pTsma->pTsma->pTags->size - 1);
690✔
7533
    tstrncpy(pCol->tableName, pTsma->targetTbName, TSDB_TABLE_NAME_LEN);
690✔
7534
    tstrncpy(pCol->tableAlias, pTsma->targetTbName, TSDB_TABLE_NAME_LEN);
690✔
7535
    pCol->tableId = pTsma->targetTbUid;
690✔
7536
    pCol->tableType = TSDB_SUPER_TABLE;
690✔
7537
    pCol->colId = pSchema->colId;
690✔
7538
    pCol->colType = COLUMN_TYPE_TAG;
690✔
7539
  } else if (code == TSDB_CODE_SUCCESS) {
114!
7540
    // if no tsma, we replace func tbname with concat('', tbname)
7541
    SFunctionNode* pFunc = (SFunctionNode*)pRewrittenFunc;
114✔
7542
    pFunc->funcId = fmGetFuncId("concat");
114✔
7543
    snprintf(pFunc->functionName, TSDB_FUNC_NAME_LEN, "concat");
114✔
7544
    code = nodesMakeNode(QUERY_NODE_VALUE, (SNode**)&pValue);
114✔
7545
    if (!pValue) code = TSDB_CODE_OUT_OF_MEMORY;
114!
7546

7547
    if (code == TSDB_CODE_SUCCESS) {
114!
7548
      pValue->translate = true;
114✔
7549
      pValue->node.resType = ((SExprNode*)(*pTbNameNode))->resType;
114✔
7550
      pValue->literal = taosMemoryCalloc(1, TSDB_TABLE_FNAME_LEN + 1);
114!
7551
      pValue->datum.p = taosMemoryCalloc(1, TSDB_TABLE_FNAME_LEN + 1 + VARSTR_HEADER_SIZE);
114!
7552
      if (!pValue->literal || !pValue->datum.p) code = terrno;
114!
7553
    }
7554

7555
    if (code == TSDB_CODE_SUCCESS) {
114!
7556
      code = nodesListMakeStrictAppend(&pFunc->pParameterList, (SNode*)pValue);
114✔
7557
      pValue = NULL;
114✔
7558
    }
7559
    if (code == TSDB_CODE_SUCCESS) {
114!
7560
      code = nodesListStrictAppend(pFunc->pParameterList, *pTbNameNode);
114✔
7561
    }
7562
  }
7563

7564
  if (code == TSDB_CODE_SUCCESS) {
804!
7565
    *pTbNameNode = (SNode*)pRewrittenFunc;
804✔
7566
  } else {
7567
    nodesDestroyNode((SNode*)pRewrittenFunc);
×
7568
    if (pValue) nodesDestroyNode((SNode*)pValue);
×
7569
  }
7570

7571
  return code;
804✔
7572
}
7573

7574
struct TsmaOptRewriteCtx {
7575
  const STSMAOptCtx*        pTsmaOptCtx;
7576
  const STSMAOptUsefulTsma* pTsma;
7577
  bool                      rewriteTag;
7578
  bool                      rewriteTbname;
7579
  int32_t                   code;
7580
};
7581

7582
EDealRes tsmaOptNodeRewriter(SNode** ppNode, void* ctx) {
4,724✔
7583
  SNode*                    pNode = *ppNode;
4,724✔
7584
  int32_t                   code = 0;
4,724✔
7585
  struct TsmaOptRewriteCtx* pCtx = ctx;
4,724✔
7586
  if (pCtx->rewriteTag && nodeType(pNode) == QUERY_NODE_COLUMN && ((SColumnNode*)pNode)->colType == COLUMN_TYPE_TAG) {
4,724✔
7587
    code = tsmaOptRewriteTag(pCtx->pTsmaOptCtx, pCtx->pTsma, (SColumnNode*)pNode);
1,512✔
7588
  } else if (pCtx->rewriteTbname &&
3,212!
7589
             ((nodeType(pNode) == QUERY_NODE_FUNCTION && ((SFunctionNode*)pNode)->funcType == FUNCTION_TYPE_TBNAME) ||
3,212✔
7590
              (nodeType(pNode) == QUERY_NODE_COLUMN && ((SColumnNode*)pNode)->colType == COLUMN_TYPE_TBNAME))) {
2,638✔
7591
    code = tsmaOptRewriteTbname(pCtx->pTsmaOptCtx, ppNode, pCtx->pTsma);
804✔
7592
    if (code == TSDB_CODE_SUCCESS) return DEAL_RES_IGNORE_CHILD;
804!
7593
  }
7594
  if (code) {
3,920!
7595
    pCtx->code = code;
×
7596
    return DEAL_RES_ERROR;
×
7597
  }
7598
  return DEAL_RES_CONTINUE;
3,920✔
7599
}
7600

7601
static int32_t tsmaOptRewriteNode(SNode** pNode, STSMAOptCtx* pCtx, const STSMAOptUsefulTsma* pTsma, bool rewriteTbName,
2,158✔
7602
                                  bool rewriteTag) {
7603
  struct TsmaOptRewriteCtx ctx = {
2,158✔
7604
      .pTsmaOptCtx = pCtx, .pTsma = pTsma, .rewriteTag = rewriteTag, .rewriteTbname = rewriteTbName, .code = 0};
7605
  SNode* pOut = *pNode;
2,158✔
7606
  nodesRewriteExpr(&pOut, tsmaOptNodeRewriter, &ctx);
2,158✔
7607
  if (ctx.code == TSDB_CODE_SUCCESS) *pNode = pOut;
2,158!
7608
  return ctx.code;
2,158✔
7609
}
7610

7611
static int32_t tsmaOptRewriteNodeList(SNodeList* pNodes, STSMAOptCtx* pCtx, const STSMAOptUsefulTsma* pTsma,
1,728✔
7612
                                      bool rewriteTbName, bool rewriteTag) {
7613
  int32_t code = 0;
1,728✔
7614
  SNode*  pNode;
7615
  FOREACH(pNode, pNodes) {
3,022✔
7616
    SNode* pOut = pNode;
1,294✔
7617
    code = tsmaOptRewriteNode(&pOut, pCtx, pTsma, rewriteTbName, rewriteTag);
1,294✔
7618
    if (TSDB_CODE_SUCCESS != code) break;
1,294!
7619
    REPLACE_NODE(pOut);
1,294✔
7620
  }
7621
  return code;
1,728✔
7622
}
7623

7624
static int32_t tsmaOptRewriteScan(STSMAOptCtx* pTsmaOptCtx, SScanLogicNode* pNewScan, const STSMAOptUsefulTsma* pTsma) {
1,072✔
7625
  SNode*  pNode;
7626
  int32_t code = 0;
1,072✔
7627

7628
  pNewScan->scanRange.skey = pTsma->scanRange.skey;
1,072✔
7629
  pNewScan->scanRange.ekey = pTsma->scanRange.ekey;
1,072✔
7630

7631
  if (pTsma->pTsma) {
1,072✔
7632
    // PK col
7633
    SColumnNode* pPkTsCol = NULL;
864✔
7634
    FOREACH(pNode, pNewScan->pScanCols) {
936!
7635
      SColumnNode* pCol = (SColumnNode*)pNode;
936✔
7636
      if (pCol->colId == PRIMARYKEY_TIMESTAMP_COL_ID) {
936✔
7637
        pPkTsCol = NULL;
864✔
7638
        code = nodesCloneNode((SNode*)pCol, (SNode**)&pPkTsCol);
864✔
7639
        break;
864✔
7640
      }
7641
    }
7642
    if (code == TSDB_CODE_SUCCESS) {
864!
7643
      nodesDestroyList(pNewScan->pScanCols);
864✔
7644
      // normal cols
7645
      pNewScan->pScanCols = NULL;
864✔
7646
      code = tsmaOptCreateTsmaScanCols(pTsma, pTsmaOptCtx->pAggFuncs, &pNewScan->pScanCols);
864✔
7647
    }
7648
    if (code == TSDB_CODE_SUCCESS && pPkTsCol) {
864!
7649
      tstrncpy(pPkTsCol->tableName, pTsma->targetTbName, TSDB_TABLE_NAME_LEN);
864✔
7650
      tstrncpy(pPkTsCol->tableAlias, pTsma->targetTbName, TSDB_TABLE_NAME_LEN);
864✔
7651
      pPkTsCol->tableId = pTsma->targetTbUid;
864✔
7652
      code = nodesListMakeStrictAppend(&pNewScan->pScanCols, (SNode*)pPkTsCol);
864✔
7653
    } else if (pPkTsCol) {
×
7654
      nodesDestroyNode((SNode*)pPkTsCol);
×
7655
    }
7656
    if (code == TSDB_CODE_SUCCESS) {
864!
7657
      pNewScan->stableId = pTsma->pTsma->destTbUid;
864✔
7658
      pNewScan->tableId = pTsma->targetTbUid;
864✔
7659
      tstrncpy(pNewScan->tableName.tname, pTsma->targetTbName, TSDB_TABLE_NAME_LEN);
864✔
7660
    }
7661
    if (code == TSDB_CODE_SUCCESS) {
864!
7662
      code = tsmaOptRewriteNodeList(pNewScan->pScanPseudoCols, pTsmaOptCtx, pTsma, true, true);
864✔
7663
    }
7664
    if (code == TSDB_CODE_SUCCESS) {
864!
7665
      code = tsmaOptRewriteNode(&pNewScan->pTagCond, pTsmaOptCtx, pTsma, true, true);
864✔
7666
    }
7667
    if (code == TSDB_CODE_SUCCESS) {
864!
7668
      code = tsmaOptRewriteNodeList(pNewScan->pGroupTags, pTsmaOptCtx, pTsma, true, true);
864✔
7669
    }
7670
    if (TSDB_CODE_SUCCESS == code) {
864!
7671
      pTsmaOptCtx->pScan->dataRequired = FUNC_DATA_REQUIRED_DATA_LOAD;
864✔
7672
      if (pTsmaOptCtx->pScan->pTsmaTargetTbVgInfo && pTsmaOptCtx->pScan->pTsmaTargetTbVgInfo->size > 0) {
864!
7673
        for (int32_t i = 0; i < taosArrayGetSize(pTsmaOptCtx->pScan->pTsmas); ++i) {
394!
7674
          STableTSMAInfo* pTsmaInfo = taosArrayGetP(pTsmaOptCtx->pScan->pTsmas, i);
394✔
7675
          if (pTsmaInfo == pTsma->pTsma) {
394✔
7676
            const SVgroupsInfo* pVgpsInfo = taosArrayGetP(pTsmaOptCtx->pScan->pTsmaTargetTbVgInfo, i);
216✔
7677
            taosMemoryFreeClear(pNewScan->pVgroupList);
216!
7678
            int32_t len = sizeof(int32_t) + sizeof(SVgroupInfo) * pVgpsInfo->numOfVgroups;
216✔
7679
            pNewScan->pVgroupList = taosMemoryCalloc(1, len);
216!
7680
            if (!pNewScan->pVgroupList) {
216!
7681
              code = terrno;
×
7682
              break;
×
7683
            }
7684
            memcpy(pNewScan->pVgroupList, pVgpsInfo, len);
216✔
7685
            break;
216✔
7686
          }
7687
        }
7688
      }
7689
    }
7690
  } else {
7691
    FOREACH(pNode, pNewScan->pGroupTags) {
530✔
7692
      // rewrite tbname recursively
7693
      struct TsmaOptRewriteCtx ctx = {
322✔
7694
          .pTsmaOptCtx = pTsmaOptCtx, .pTsma = NULL, .rewriteTag = false, .rewriteTbname = true, .code = 0};
7695
      nodesRewriteExpr(&pNode, tsmaOptNodeRewriter, &ctx);
322✔
7696
      if (ctx.code) {
322!
7697
        code = ctx.code;
×
7698
      } else {
7699
        REPLACE_NODE(pNode);
322✔
7700
      }
7701
    }
7702
  }
7703
  return code;
1,072✔
7704
}
7705

7706
static int32_t tsmaOptCreateWStart(int8_t precision, SFunctionNode** pWStartOut) {
×
7707
  SFunctionNode* pWStart = NULL;
×
7708
  int32_t        code = nodesMakeNode(QUERY_NODE_FUNCTION, (SNode**)&pWStart);
×
7709
  if (NULL == pWStart) {
×
7710
    return code;
×
7711
  }
7712
  tstrncpy(pWStart->functionName, "_wstart", TSDB_FUNC_NAME_LEN);
×
7713
  int64_t pointer = (int64_t)pWStart;
×
7714
  char    name[TSDB_COL_NAME_LEN + TSDB_POINTER_PRINT_BYTES + TSDB_NAME_DELIMITER_LEN + 1] = {0};
×
7715
  int32_t len = tsnprintf(name, sizeof(name) - 1, "%s.%" PRId64, pWStart->functionName, pointer);
×
7716
  (void)taosHashBinary(name, len);
7717
  tstrncpy(pWStart->node.aliasName, name, TSDB_COL_NAME_LEN);
×
7718
  pWStart->node.resType.precision = precision;
×
7719

7720
  code = fmGetFuncInfo(pWStart, NULL, 0);
×
7721
  if (code) {
×
7722
    nodesDestroyNode((SNode*)pWStart);
×
7723
  } else {
7724
    *pWStartOut = pWStart;
×
7725
  }
7726
  return code;
×
7727
}
7728

7729
static int32_t tsmaOptRewriteParent(STSMAOptCtx* pTsmaOptCtx, SLogicNode* pParent, SScanLogicNode* pScan,
864✔
7730
                                    const STSMAOptUsefulTsma* pTsma) {
7731
  int32_t           code = 0;
864✔
7732
  SColumnNode*      pColNode;
7733
  SWindowLogicNode* pWindow = NULL;
864✔
7734
  SAggLogicNode*    pAgg;
7735
  SNodeList*        pAggFuncs;
7736
  SListCell*        pScanListCell;
7737
  SNode*            pAggFuncNode;
7738
  SNodeList*        pAggStateFuncs = NULL;
864✔
7739
  bool              isFirstMergeNode = pTsmaOptCtx->pScan == pScan;
864✔
7740
  SFunctionNode *   pPartial = NULL, *pMerge = NULL;
864✔
7741

7742
  if (nodeType(pParent) == QUERY_NODE_LOGIC_PLAN_WINDOW) {
864✔
7743
    pWindow = (SWindowLogicNode*)pParent;
654✔
7744
    pAggFuncs = pWindow->pFuncs;
654✔
7745
  } else {
7746
    pAgg = (SAggLogicNode*)pParent;
210✔
7747
    pAggFuncs = pAgg->pAggFuncs;
210✔
7748
  }
7749
  pScanListCell = pScan->pScanCols->pHead;
864✔
7750

7751
  FOREACH(pAggFuncNode, pAggFuncs) {
38,221!
7752
    SFunctionNode* pAggFunc = (SFunctionNode*)pAggFuncNode;
37,357✔
7753
    if (fmIsGroupKeyFunc(pAggFunc->funcId)) {
37,357✔
7754
      struct TsmaOptRewriteCtx ctx = {
522✔
7755
          .pTsmaOptCtx = pTsmaOptCtx, .pTsma = pTsma, .rewriteTag = true, .rewriteTbname = true, .code = 0};
7756
      nodesRewriteExpr(&pAggFuncNode, tsmaOptNodeRewriter, &ctx);
522✔
7757
      if (ctx.code) {
522!
7758
        code = ctx.code;
×
7759
        break;
×
7760
      } else {
7761
        REPLACE_NODE(pAggFuncNode);
522✔
7762
      }
7763
      continue;
522✔
7764
    } else if (fmIsPseudoColumnFunc(pAggFunc->funcId)) {
36,835✔
7765
      continue;
376✔
7766
    }
7767
    code = fmGetDistMethod(pAggFunc, &pPartial, NULL, &pMerge);
36,459✔
7768
    if (code) break;
36,459!
7769

7770
    pColNode = (SColumnNode*)pScanListCell->pNode;
36,459✔
7771
    pScanListCell = pScanListCell->pNext;
36,459✔
7772
    pColNode->node.resType = pPartial->node.resType;
36,459✔
7773
    // currently we assume that the first parameter must be the scan column
7774
    (void)nodesListErase(pMerge->pParameterList, pMerge->pParameterList->pHead);
36,459✔
7775
    SNode* pNew = NULL;
36,459✔
7776
    code = nodesCloneNode((SNode*)pColNode, &pNew);
36,459✔
7777
    if (TSDB_CODE_SUCCESS == code) {
36,459!
7778
      code = nodesListPushFront(pMerge->pParameterList, pNew);
36,459✔
7779
    }
7780
    nodesDestroyNode((SNode*)pPartial);
36,459✔
7781
    if (TSDB_CODE_SUCCESS != code) {
36,459!
7782
      nodesDestroyNode(pNew);
×
7783
      break;
×
7784
    }
7785

7786
    REPLACE_NODE(pMerge);
36,459✔
7787
  }
7788

7789
  if (code == TSDB_CODE_SUCCESS && pWindow) {
864!
7790
    SColumnNode* pCol = (SColumnNode*)pScan->pScanCols->pTail->pNode;
654✔
7791
    nodesDestroyNode(pWindow->pTspk);
654✔
7792
    pWindow->pTspk = NULL;
654✔
7793
    code = nodesCloneNode((SNode*)pCol, &pWindow->pTspk);
654✔
7794
  }
7795

7796
  if (code == TSDB_CODE_SUCCESS) {
864!
7797
    nodesDestroyList(pScan->node.pTargets);
864✔
7798
    code = createColumnByRewriteExprs(pScan->pScanCols, &pScan->node.pTargets);
864✔
7799
  }
7800
  if (code == TSDB_CODE_SUCCESS) {
864!
7801
    code = createColumnByRewriteExprs(pScan->pScanPseudoCols, &pScan->node.pTargets);
864✔
7802
  }
7803

7804
  return code;
864✔
7805
}
7806

7807
static int32_t tsmaOptGeneratePlan(STSMAOptCtx* pTsmaOptCtx) {
812✔
7808
  int32_t                   code = 0;
812✔
7809
  const STSMAOptUsefulTsma* pTsma = NULL;
812✔
7810
  SNodeList*                pAggFuncs = NULL;
812✔
7811
  bool                      hasSubPlan = false;
812✔
7812

7813
  for (int32_t i = 0; i < pTsmaOptCtx->pUsedTsmas->size; ++i) {
1,884✔
7814
    STSMAOptUsefulTsma* pTsma = taosArrayGet(pTsmaOptCtx->pUsedTsmas, i);
1,072✔
7815
    if (!pTsma->pTsma) continue;
1,072✔
7816
    if (pTsmaOptCtx->pScan->tableType == TSDB_CHILD_TABLE || pTsmaOptCtx->pScan->tableType == TSDB_NORMAL_TABLE) {
864✔
7817
      for (int32_t j = 0; j < pTsmaOptCtx->pScan->pTsmas->size; ++j) {
776✔
7818
        if (taosArrayGetP(pTsmaOptCtx->pScan->pTsmas, j) == pTsma->pTsma) {
580✔
7819
          const STsmaTargetTbInfo* ptbInfo = taosArrayGet(pTsmaOptCtx->pScan->pTsmaTargetTbInfo, j);
196✔
7820
          tstrncpy(pTsma->targetTbName, ptbInfo->tableName, TSDB_TABLE_NAME_LEN);
196✔
7821
          pTsma->targetTbUid = ptbInfo->uid;
196✔
7822
        }
7823
      }
7824
    } else {
7825
      tstrncpy(pTsma->targetTbName, pTsma->pTsma->targetTb, TSDB_TABLE_NAME_LEN);
668✔
7826
      pTsma->targetTbUid = pTsma->pTsma->destTbUid;
668✔
7827
    }
7828
  }
7829

7830
  for (int32_t i = 1; i < pTsmaOptCtx->pUsedTsmas->size && code == TSDB_CODE_SUCCESS; ++i) {
1,072!
7831
    pTsma = taosArrayGet(pTsmaOptCtx->pUsedTsmas, i);
260✔
7832
    SLogicSubplan* pSubplan = NULL;
260✔
7833
    code = nodesMakeNode(QUERY_NODE_LOGIC_SUBPLAN, (SNode**)&pSubplan);
260✔
7834
    if (!pSubplan) {
260!
7835
      break;
×
7836
    }
7837
    pSubplan->subplanType = SUBPLAN_TYPE_SCAN;
260✔
7838
    pTsmaOptCtx->generatedSubPlans[i - 1] = pSubplan;
260✔
7839
    hasSubPlan = true;
260✔
7840
    SLogicNode* pParent = NULL;
260✔
7841
    code = nodesCloneNode((SNode*)pTsmaOptCtx->pParent, (SNode**)&pParent);
260✔
7842
    if (!pParent) {
260!
7843
      break;
×
7844
    }
7845
    pSubplan->pNode = pParent;
260✔
7846
    pParent->pParent = NULL;
260✔
7847
    pParent->groupAction = GROUP_ACTION_KEEP;
260✔
7848
    SScanLogicNode* pScan = (SScanLogicNode*)pParent->pChildren->pHead->pNode;
260✔
7849
    code = tsmaOptRewriteScan(pTsmaOptCtx, pScan, pTsma);
260✔
7850
    if (code == TSDB_CODE_SUCCESS && pTsma->pTsma) {
260!
7851
      code = tsmaOptRewriteParent(pTsmaOptCtx, pParent, pScan, pTsma);
178✔
7852
    }
7853
  }
7854

7855
  if (code == TSDB_CODE_SUCCESS) {
812!
7856
    pTsma = taosArrayGet(pTsmaOptCtx->pUsedTsmas, 0);
812✔
7857
    pTsmaOptCtx->pScan->needSplit = hasSubPlan;
812✔
7858
    code = tsmaOptRewriteScan(pTsmaOptCtx, pTsmaOptCtx->pScan, pTsma);
812✔
7859
    if (code == TSDB_CODE_SUCCESS && pTsma->pTsma) {
812!
7860
      code = tsmaOptRewriteParent(pTsmaOptCtx, pTsmaOptCtx->pParent, pTsmaOptCtx->pScan, pTsma);
686✔
7861
    }
7862
  }
7863

7864
  return code;
812✔
7865
}
7866

7867
static bool tsmaOptIsUsingTsmas(STSMAOptCtx* pCtx) {
950✔
7868
  if (pCtx->pUsedTsmas->size == 0) {
950✔
7869
    return false;
30✔
7870
  }
7871
  for (int32_t i = 0; i < pCtx->pUsedTsmas->size; ++i) {
1,160✔
7872
    const STSMAOptUsefulTsma* pTsma = taosArrayGet(pCtx->pUsedTsmas, i);
1,052✔
7873
    if (pTsma->pTsma) return true;
1,052✔
7874
  }
7875
  return false;
108✔
7876
}
7877

7878
static int32_t tsmaOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
946,211✔
7879
  int32_t         code = 0;
946,211✔
7880
  STSMAOptCtx     tsmaOptCtx = {0};
946,211✔
7881
  SScanLogicNode* pScan = (SScanLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, tsmaOptMayBeOptimized, &code);
946,211✔
7882
  if (!pScan) return code;
946,313✔
7883

7884
  SLogicNode* pRootNode = getLogicNodeRootNode((SLogicNode*)pScan);
1,483✔
7885
  if (getOptHint(pRootNode->pHint, HINT_SKIP_TSMA)) return code;
1,484!
7886

7887
  code = fillTSMAOptCtx(&tsmaOptCtx, pScan, pCxt->pPlanCxt->timezone);
1,484✔
7888
  if (code == TSDB_CODE_SUCCESS) {
1,484!
7889
    // 1. extract useful tsmas
7890
    code = tsmaOptFilterTsmas(&tsmaOptCtx);
1,484✔
7891

7892
    if (code == TSDB_CODE_SUCCESS && tsmaOptCtx.pUsefulTsmas->size > 0) {
1,484!
7893
      // 2. sort useful tsmas with interval
7894
      taosArraySort(tsmaOptCtx.pUsefulTsmas, tsmaInfoCompWithIntervalDesc);
950✔
7895
      // 3. split windows
7896
      code = tsmaOptSplitWindows(&tsmaOptCtx, tsmaOptCtx.pTimeRange, pCxt->pPlanCxt->timezone);
950✔
7897
      if (TSDB_CODE_SUCCESS == code && tsmaOptIsUsingTsmas(&tsmaOptCtx)) {
950!
7898
        // 4. create logic plan
7899
        code = tsmaOptGeneratePlan(&tsmaOptCtx);
812✔
7900

7901
        if (TSDB_CODE_SUCCESS == code) {
812!
7902
          for (int32_t i = 0; i < 2 && (TSDB_CODE_SUCCESS == code); i++) {
2,436!
7903
            SLogicSubplan* pSubplan = tsmaOptCtx.generatedSubPlans[i];
1,624✔
7904
            if (!pSubplan) continue;
1,624✔
7905
            pSubplan->subplanType = SUBPLAN_TYPE_SCAN;
260✔
7906
            code = nodesListMakeAppend(tsmaOptCtx.ppParentTsmaSubplans, (SNode*)pSubplan);
260✔
7907
          }
7908
          pCxt->optimized = true;
812✔
7909
        }
7910
      }
7911
    }
7912
  }
7913
  pScan->pTsmas = NULL;
1,484✔
7914
  clearTSMAOptCtx(&tsmaOptCtx);
1,484✔
7915
  return code;
1,484✔
7916
}
7917

7918
static bool eliminateVirtualScanMayBeOptimized(SLogicNode* pNode, void* pCtx) {
3,188,464✔
7919
  if (NULL == pNode->pParent) {
3,188,464✔
7920
    return false;
945,436✔
7921
  }
7922

7923
  if (nodeType(pNode) != QUERY_NODE_LOGIC_PLAN_VIRTUAL_TABLE_SCAN || LIST_LENGTH(pNode->pChildren) != 1) {
2,243,028!
7924
    return false;
2,238,492✔
7925
  }
7926

7927
  if (pNode->pConditions || ((SVirtualScanLogicNode *)pNode)->pScanPseudoCols) {
4,536✔
7928
    return false;
2,014✔
7929
  }
7930

7931
  SVirtualScanLogicNode* pVScan = (SVirtualScanLogicNode *)pNode;
2,522✔
7932
  SNode*                 pCol;
7933
  FOREACH(pCol, pVScan->pScanCols) {
3,710!
7934
    SColumnNode* pScanCol = (SColumnNode* )pCol;
3,098✔
7935
    if (!pScanCol->hasRef) {
3,098✔
7936
      // if col don't have ref, it can't be eliminated. If the vscan operator is eliminated,
7937
      // the upper operator can't find the right slot id.
7938
      return false;
1,910✔
7939
    }
7940
  }
7941
  return true;
612✔
7942
}
7943

7944
static int32_t eliminateVirtualScanOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan,
612✔
7945
                                                SLogicNode* pVirtualScanNode) {
7946
  SNode* pTargets = NULL;
612✔
7947
  SNode* pSibling = NULL;
612✔
7948

7949
  FOREACH(pSibling, pVirtualScanNode->pParent->pChildren) {
612!
7950
    if (nodesEqualNode(pSibling, (SNode*)pVirtualScanNode)) {
612!
7951
      SNode* pChild;
7952
      FOREACH(pChild, pVirtualScanNode->pChildren) {
1,224!
7953
        // clear the mask to try scanPathOptimize again.
7954
        OPTIMIZE_FLAG_CLEAR_MASK(((SScanLogicNode*)pChild)->node.optimizedFlag, OPTIMIZE_FLAG_SCAN_PATH);
612✔
7955
        ((SLogicNode*)pChild)->pParent = pVirtualScanNode->pParent;
612✔
7956
      }
7957
      INSERT_LIST(pVirtualScanNode->pParent->pChildren, pVirtualScanNode->pChildren);
612✔
7958
      pVirtualScanNode->pChildren = NULL;
612✔
7959

7960
      ERASE_NODE(pVirtualScanNode->pParent->pChildren);
612✔
7961
      pCxt->optimized = true;
612✔
7962
      return TSDB_CODE_SUCCESS;
612✔
7963
    }
7964
  }
7965

7966
  return TSDB_CODE_PLAN_INTERNAL_ERROR;
×
7967
}
7968

7969
// eliminate virtual scan node when it has only one child
7970
static int32_t eliminateVirtualScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
945,405✔
7971
  SLogicNode* pVirtualScanNode = optFindPossibleNode(pLogicSubplan->pNode, eliminateVirtualScanMayBeOptimized, NULL);
945,405✔
7972
  if (NULL == pVirtualScanNode) {
945,503✔
7973
    return TSDB_CODE_SUCCESS;
944,891✔
7974
  }
7975

7976
  return eliminateVirtualScanOptimizeImpl(pCxt, pLogicSubplan, pVirtualScanNode);
612✔
7977
}
7978

7979
static bool pdaMayBeOptimized(SLogicNode* pNode, void* pCtx) {
3,186,782✔
7980
  if (QUERY_NODE_LOGIC_PLAN_AGG != nodeType(pNode) || 1 != LIST_LENGTH(pNode->pChildren) ||
3,186,782!
7981
      QUERY_NODE_LOGIC_PLAN_VIRTUAL_TABLE_SCAN != nodeType(nodesListGetNode(pNode->pChildren, 0))) {
624,633✔
7982
    return false;
3,185,973✔
7983
  }
7984

7985
  // virtual table scan should have more than one child.
7986
  if (LIST_LENGTH(((SVirtualScanLogicNode *)nodesListGetNode(pNode->pChildren, 0))->node.pChildren) < 2) {
829!
7987
    return false;
168✔
7988
  }
7989

7990
  SVirtualScanLogicNode* pVScan = (SVirtualScanLogicNode *)nodesListGetNode(pNode->pChildren, 0);
724✔
7991
  SNode*                 pCol;
7992
  FOREACH(pCol, pVScan->pScanCols) {
2,324✔
7993
    SColumnNode* pScanCol = (SColumnNode* )pCol;
2,148✔
7994
    if (!pScanCol->hasRef) {
2,148✔
7995
      // if col don't have ref, it can't be eliminated. If the vscan operator is eliminated,
7996
      // the upper operator can't find the right slot id.
7997
      return false;
548✔
7998
    }
7999
  }
8000

8001
  SAggLogicNode*  pAgg = (SAggLogicNode*)pNode;
176✔
8002
  SNode*          pAggFunc = NULL;
176✔
8003
  FOREACH(pAggFunc, pAgg->pAggFuncs) {
836✔
8004
    SFunctionNode *pFunc = (SFunctionNode *)pAggFunc;
680✔
8005
    if (fmIsSelectFunc(pFunc->funcId) || fmIsGroupKeyFunc(pFunc->funcId)) {
680!
8006
      return false;
20✔
8007
    }
8008
  }
8009

8010
  if (pAgg->hasGroup || 0 != LIST_LENGTH(pAgg->pTsmaSubplans) || NULL != pAgg->node.pConditions ||
156!
8011
      NULL != pAgg->node.pLimit || NULL != pAgg->node.pSlimit) {
36!
8012
    return false;
120✔
8013
  }
8014

8015
  return true;
36✔
8016
}
8017

8018
static int32_t findDepTableScanNode(SColumnNode* pCol, SVirtualScanLogicNode *pVScan, SNode **ppNode) {
×
8019
  int32_t code = TSDB_CODE_SUCCESS;
×
8020
  SNode  *pScan = NULL;
×
8021
  FOREACH(pScan, pVScan->node.pChildren) {
×
8022
    SScanLogicNode *pScanNode = (SScanLogicNode *)pScan;
×
8023
    SNode *pScanCol = NULL;
×
8024
    FOREACH(pScanCol, pScanNode->pScanCols) {
×
8025
      if (QUERY_NODE_COLUMN == nodeType(pScanCol)) {
×
8026
        SColumnNode *pScanColNode = (SColumnNode *)pScanCol;
×
8027
        if (pScanColNode->colId == PRIMARYKEY_TIMESTAMP_COL_ID) {
×
8028
          continue;
×
8029
        }
8030
        if (pScanColNode->hasDep && pCol->hasRef) {
×
8031
          if (strcmp(pScanColNode->dbName, pCol->refDbName) == 0 &&
×
8032
              strcmp(pScanColNode->tableAlias, pCol->refTableName) == 0 &&
×
8033
              strcmp(pScanColNode->colName, pCol->refColName) == 0) {
×
8034
            PLAN_RET(nodesCloneNode(pScan, ppNode));
×
8035
          }
8036
        } else {
8037
          // TODO(smj): make a proper error code.
8038
          *ppNode = NULL;
×
8039
          planError("column %s.%s has no depend column", pCol->tableAlias, pCol->colName);
×
8040
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
×
8041
        }
8042
      }
8043
    }
8044
  }
8045
  *ppNode = NULL;
×
8046
  planError("column %s.%s's depend column not found in virtual scan node", pCol->tableAlias, pCol->colName);
×
8047
  // TODO(smj): make a proper error code.
8048
  return TSDB_CODE_PLAN_INTERNAL_ERROR;
×
8049
}
8050

8051
static int32_t mergeAggFuncToAggNode(SAggLogicNode* pAgg, SFunctionNode* pFunc) {
×
8052
  int32_t code = TSDB_CODE_SUCCESS;
×
8053
  SNode  *ppFuncNode = NULL;
×
8054
  PLAN_ERR_JRET(nodesCloneNode((SNode *)pFunc, &ppFuncNode));
×
8055
  PLAN_ERR_JRET(nodesListMakeAppend(&pAgg->pAggFuncs, (SNode *)ppFuncNode));
×
8056
  pAgg->hasLast |= fmIsLastFunc(((SFunctionNode *)ppFuncNode)->funcId);
×
8057
  pAgg->hasLastRow |= fmIsLastRowFunc(((SFunctionNode *)ppFuncNode)->funcId);
×
8058
  pAgg->hasTimeLineFunc |= fmIsTimelineFunc(((SFunctionNode *)ppFuncNode)->funcId);
×
8059
  pAgg->onlyHasKeepOrderFunc &= fmIsKeepOrderFunc(((SFunctionNode *)ppFuncNode)->funcId);
×
8060
  pAgg->hasGroupKeyOptimized = false;
×
8061
  pAgg->hasGroup = false;
×
8062
  pAgg->isGroupTb = false;
×
8063
  pAgg->isPartTb = false;
×
8064
  return code;
×
8065
_return:
×
8066
  nodesDestroyNode(ppFuncNode);
×
8067
  return code;
×
8068
}
8069

8070
static int32_t rebuildPlanForPdaOptimize(SColumnNode* pCol, SFunctionNode* pAggFunc, SVirtualScanLogicNode* pVScan,
×
8071
                                         SHashObj* pAggNodeMap) {
8072
  int32_t         code = TSDB_CODE_SUCCESS;
×
8073
  SScanLogicNode* pDepScan = NULL;
×
8074
  SAggLogicNode*  pAggNode = NULL;
×
8075
  bool            append = false;
×
8076
  SAggLogicNode** pNodeFound = NULL;
×
8077

8078
  // pAggNodeMap is a hash map, the key is the vtable's origin table's name, the value is the SAggLogicNode ptr.
8079
  // if 2 more agg func has the same origin table, we should merge them into one agg node.
8080
  PLAN_ERR_JRET(findDepTableScanNode(pCol, pVScan, (SNode**)&pDepScan));
×
8081
  char tableFNameKey[TSDB_COL_FNAME_LEN + 1] = {0};
×
8082
  TAOS_STRNCAT(tableFNameKey, pDepScan->tableName.tname, TSDB_TABLE_NAME_LEN);
×
8083
  TAOS_STRNCAT(tableFNameKey, ".", 2);
×
8084
  TAOS_STRNCAT(tableFNameKey, pCol->colName, TSDB_COL_NAME_LEN);
×
8085

8086
  pNodeFound = (SAggLogicNode **)taosHashGet(pAggNodeMap, &tableFNameKey, strlen(tableFNameKey));
×
8087
  if (NULL == pNodeFound) {
×
8088
    // make new agg node.
8089
    PLAN_ERR_JRET(nodesMakeNode(QUERY_NODE_LOGIC_PLAN_AGG, (SNode**)&pAggNode));
×
8090
    PLAN_ERR_JRET(mergeAggFuncToAggNode(pAggNode, pAggFunc));
×
8091
    PLAN_ERR_JRET(nodesListMakeAppend(&pAggNode->node.pChildren, (SNode*)pDepScan));
×
8092
    append = true;
×
8093
    PLAN_ERR_JRET(taosHashPut(pAggNodeMap, &tableFNameKey, strlen(tableFNameKey), &pAggNode, POINTER_BYTES));
×
8094
  } else {
8095
    pAggNode = *pNodeFound;
×
8096
    // merge the agg func to the existed agg node.
8097
    PLAN_ERR_JRET(mergeAggFuncToAggNode(pAggNode, pAggFunc));
×
8098
    nodesDestroyNode((SNode*)pDepScan);
×
8099
  }
8100
  return code;
×
8101
_return:
×
8102
  if (!append) {
×
8103
    nodesDestroyNode((SNode*)pDepScan);
×
8104
  }
8105
  return code;
×
8106
}
8107

8108
static void destroyAggLogicNode(void* data) {
×
8109
  if (data == NULL) {
×
8110
    return;
×
8111
  }
8112
  SAggLogicNode* pNode = *(SAggLogicNode **)data;
×
8113
  nodesDestroyNode((SNode*)pNode);
×
8114
}
8115

8116
// This optimization rule will optimize plan tree from
8117
// Agg -> VirtualScan -> TableScan
8118
//                    \> TableScan
8119
//                    \> TableScan
8120
// to
8121
// Merge -> Agg -> TableScan
8122
//       \> Agg -> TableScan
8123
//       \> Agg -> TableScan
8124

8125
static int32_t pdaOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
944,789✔
8126
  int32_t          code = TSDB_CODE_SUCCESS;
944,789✔
8127
  SMergeLogicNode* pMerge = NULL;
944,789✔
8128
  SAggLogicNode*   pAgg = (SAggLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, pdaMayBeOptimized, &code);
944,789✔
8129
  if (NULL == pAgg) {
944,892✔
8130
    return code;
944,856✔
8131
  }
8132

8133
  SVirtualScanLogicNode *pVScan = (SVirtualScanLogicNode*)nodesListGetNode(pAgg->node.pChildren, 0);
36✔
8134
  SNode                 *pAggFunc = NULL;
36✔
8135
  SHashObj              *pAggNodeMap = taosHashInit(LIST_LENGTH(pAgg->pAggFuncs), taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_ENTRY_LOCK);
36!
8136
  if (NULL == pAggNodeMap) {
36!
8137
    PLAN_ERR_JRET(terrno);
×
8138
  }
8139

8140
  FOREACH(pAggFunc, pAgg->pAggFuncs) {
36!
8141
    SFunctionNode *pFunc = (SFunctionNode *)pAggFunc;
36✔
8142
    SNode         *pParam = NULL;
36✔
8143
    FOREACH(pParam, pFunc->pParameterList) {
36!
8144
      if (QUERY_NODE_COLUMN == nodeType(pParam)) {
36!
8145
        SColumnNode *pCol = (SColumnNode *)pParam;
36✔
8146
        if (pCol->colType == COLUMN_TYPE_TAG) {
36!
8147
          // If the column is a tag column, we should not optimize the aggregation. Since tag will be read from the
8148
          // virtual tablescan operator.
8149
          goto _return;
36✔
8150
        }
8151
        PLAN_ERR_JRET(rebuildPlanForPdaOptimize(pCol, pFunc, pVScan, pAggNodeMap));
×
8152
      }
8153
    }
8154
  }
8155

8156
  if (taosHashGetSize(pAggNodeMap) != LIST_LENGTH(pVScan->node.pChildren)) {
×
8157
      // The number of agg nodes should be equal to the number of virtual scan nodes.
8158
      // If not, it means that some virtual scan nodes are not used in the aggregation.
8159
      // In this case, we should not optimize the aggregation.
8160
      goto _return;
×
8161
  }
8162

8163

8164
  PLAN_ERR_JRET(nodesMakeNode(QUERY_NODE_LOGIC_PLAN_MERGE, (SNode**)&pMerge));
×
8165

8166
  pMerge->colsMerge = true;
×
8167
  pMerge->numOfChannels = LIST_LENGTH(pAgg->pAggFuncs);
×
8168
  pMerge->srcGroupId = -1;
×
8169
  pMerge->node.precision = pAgg->node.precision;
×
8170

8171
  void *pIter = NULL;
×
8172
  while ((pIter = taosHashIterate(pAggNodeMap, pIter))) {
×
8173
    SAggLogicNode **pAggNode = (SAggLogicNode**)pIter;
×
8174
    (*pAggNode)->node.pParent = (SLogicNode*)pMerge;
×
8175
    SNode* pNode = NULL;
×
8176
    FOREACH(pNode, (*pAggNode)->node.pChildren) {
×
8177
      // clear the mask to try scanPathOptimize again.
8178
      OPTIMIZE_FLAG_CLEAR_MASK(((SScanLogicNode*)pNode)->node.optimizedFlag, OPTIMIZE_FLAG_SCAN_PATH);
×
8179
      ((SLogicNode*)pNode)->pParent = (SLogicNode*)*pAggNode;
×
8180
    }
8181
    PLAN_ERR_JRET(createColumnByRewriteExprs((*pAggNode)->pAggFuncs, &(*pAggNode)->node.pTargets));
×
8182
    PLAN_ERR_JRET(nodesListMakeAppend(&pMerge->node.pChildren, (SNode*)*pAggNode));
×
8183
    FOREACH(pNode, (*pAggNode)->node.pTargets) {
×
8184
      PLAN_ERR_JRET(nodesListMakeStrictAppend(&pMerge->node.pTargets, pNode));
×
8185
    }
8186
  }
8187

8188
  PLAN_ERR_JRET(replaceLogicNode(pLogicSubplan, (SLogicNode*)pAgg, (SLogicNode*)pMerge));
×
8189

8190
  nodesDestroyNode((SNode*)pVScan);
×
8191
  nodesDestroyNode((SNode*)pAgg);
×
8192
  taosHashCleanup(pAggNodeMap);
×
8193

8194
  pCxt->optimized = true;
×
8195
  return code;
×
8196
_return:
36✔
8197
  nodesDestroyNode((SNode*)pMerge);
36✔
8198
  taosHashSetFreeFp(pAggNodeMap, destroyAggLogicNode);
36✔
8199
  taosHashCleanup(pAggNodeMap);
36✔
UNCOV
8200
  return code;
×
8201
}
8202

8203

8204
// clang-format off
8205
static const SOptimizeRule optimizeRuleSet[] = {
8206
  {.pName = "ScanPath",                   .optimizeFunc = scanPathOptimize},
8207
  {.pName = "PushDownCondition",          .optimizeFunc = pdcOptimize},
8208
  {.pName = "EliminateNotNullCond",       .optimizeFunc = eliminateNotNullCondOptimize},
8209
  {.pName = "JoinCondOptimize",           .optimizeFunc = joinCondOptimize},
8210
  {.pName = "HashJoin",                   .optimizeFunc = hashJoinOptimize},
8211
  {.pName = "StableJoin",                 .optimizeFunc = stableJoinOptimize},
8212
  {.pName = "GroupJoin",                  .optimizeFunc = groupJoinOptimize},
8213
  {.pName = "sortNonPriKeyOptimize",      .optimizeFunc = sortNonPriKeyOptimize},
8214
  {.pName = "SortPrimaryKey",             .optimizeFunc = sortPrimaryKeyOptimize},
8215
  {.pName = "SortForjoin",                .optimizeFunc = sortForJoinOptimize},
8216
  {.pName = "SmaIndex",                   .optimizeFunc = smaIndexOptimize},
8217
  {.pName = "PushDownLimit",              .optimizeFunc = pushDownLimitOptimize},
8218
  {.pName = "PartitionTags",              .optimizeFunc = partTagsOptimize},
8219
  {.pName = "MergeProjects",              .optimizeFunc = mergeProjectsOptimize},
8220
  {.pName = "RewriteTail",                .optimizeFunc = rewriteTailOptimize},
8221
  {.pName = "RewriteUnique",              .optimizeFunc = rewriteUniqueOptimize},
8222
  {.pName = "splitCacheLastFunc",         .optimizeFunc = splitCacheLastFuncOptimize},
8223
  {.pName = "LastRowScan",                .optimizeFunc = lastRowScanOptimize},
8224
  {.pName = "TagScan",                    .optimizeFunc = tagScanOptimize},
8225
  {.pName = "TableCountScan",             .optimizeFunc = tableCountScanOptimize},
8226
  {.pName = "EliminateProject",           .optimizeFunc = eliminateProjOptimize},
8227
  {.pName = "EliminateSetOperator",       .optimizeFunc = eliminateSetOpOptimize},
8228
  {.pName = "PartitionCols",              .optimizeFunc = partitionColsOpt},
8229
  {.pName = "Tsma",                       .optimizeFunc = tsmaOptimize},
8230
  {.pName = "EliminateVirtualScan",       .optimizeFunc = eliminateVirtualScanOptimize},
8231
  {.pName = "PushDownAgg",                .optimizeFunc = pdaOptimize},
8232
};
8233
// clang-format on
8234

8235
static const int32_t optimizeRuleNum = (sizeof(optimizeRuleSet) / sizeof(SOptimizeRule));
8236

8237
static int32_t dumpLogicSubplan(const char* pRuleName, SLogicSubplan* pSubplan) {
3,685,577✔
8238
  int32_t code = 0;
3,685,577✔
8239
  if (!tsQueryPlannerTrace) {
3,685,577✔
8240
    return code;
3,683,333✔
8241
  }
8242
  char* pStr = NULL;
2,244✔
8243
  code = nodesNodeToString((SNode*)pSubplan, false, &pStr, NULL);
2,244✔
8244
  if (TSDB_CODE_SUCCESS == code) {
2,269!
8245
    if (NULL == pRuleName) {
2,269✔
8246
      qDebugL("before optimize, JsonPlan: %s", pStr);
600✔
8247
    } else {
8248
      qDebugL("apply optimize %s rule, JsonPlan: %s", pRuleName, pStr);
1,669✔
8249
    }
8250
    taosMemoryFree(pStr);
2,269!
8251
  }
8252
  return code;
2,269✔
8253
}
8254

8255
static int32_t applyOptimizeRule(SPlanContext* pCxt, SLogicSubplan* pLogicSubplan) {
945,514✔
8256
  SOptimizeContext cxt = {.pPlanCxt = pCxt, .optimized = false};
945,514✔
8257
  bool             optimized = false;
945,514✔
8258
  int32_t          code = dumpLogicSubplan(NULL, pLogicSubplan);
945,514✔
8259
  if (TSDB_CODE_SUCCESS != code) {
945,545!
8260
    return code;
×
8261
  }
8262
  do {
8263
    optimized = false;
3,685,704✔
8264
    for (int32_t i = 0; i < optimizeRuleNum; ++i) {
44,967,440✔
8265
      cxt.optimized = false;
44,023,485✔
8266
      int32_t code = optimizeRuleSet[i].optimizeFunc(&cxt, pLogicSubplan);
44,023,485✔
8267
      if (TSDB_CODE_SUCCESS != code) {
44,022,553✔
8268
        return code;
671✔
8269
      }
8270
      if (cxt.optimized) {
44,021,882✔
8271
        optimized = true;
2,740,146✔
8272
        code = dumpLogicSubplan(optimizeRuleSet[i].pName, pLogicSubplan);
2,740,146✔
8273
        break;
2,740,142✔
8274
      }
8275
    }
8276
  } while (optimized && (TSDB_CODE_SUCCESS == code));
3,684,097✔
8277
  return code;
943,938✔
8278
}
8279

8280
int32_t optimizeLogicPlan(SPlanContext* pCxt, SLogicSubplan* pLogicSubplan) {
10,755,795✔
8281
  if (SUBPLAN_TYPE_MODIFY == pLogicSubplan->subplanType && NULL == pLogicSubplan->pNode->pChildren) {
10,755,795✔
8282
    return TSDB_CODE_SUCCESS;
9,854,335✔
8283
  }
8284
  return applyOptimizeRule(pCxt, pLogicSubplan);
901,460✔
8285
}
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

© 2025 Coveralls, Inc