Coverage Report

Created: 2026-10-22 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tdengine/source/libs/planner/src/planPhysiCreater.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
3
 *
4
 * SPDX-License-Identifier: AGPL-3.0-or-later
5
 *
6
 * This program is free software: you can use, redistribute, and/or modify
7
 * it under the terms of the GNU Affero General Public License, version 3
8
 * or later ("AGPL"), as published by the Free Software Foundation.
9
 *
10
 * This program is distributed in the hope that it will be useful, but WITHOUT
11
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
 * FITNESS FOR A PARTICULAR PURPOSE.
13
 *
14
 * You should have received a copy of the GNU Affero General Public License
15
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
#include <stdio.h>
19
#include <stdlib.h>
20
21
#include "extConnector.h"
22
#include "nodes.h"
23
#include "planInt.h"
24
25
#include "catalog.h"
26
#include "functionMgt.h"
27
#include "planner.h"
28
#include "scalar.h"
29
#include "plannodes.h"
30
#include "systable.h"
31
#include "tglobal.h"
32
#include "ttypes.h"
33
34
typedef struct SSlotIdInfo {
35
  int16_t slotId;
36
  bool    set;
37
} SSlotIdInfo;
38
39
0
static bool fqDiagEnabled(void) {
40
0
  return NULL != getenv("FQ_DIAG");
41
0
}
42
43
0
static void fqDiagDumpExprList(const char* pTag, const SNodeList* pExprs) {
44
0
  if (!fqDiagEnabled()) {
45
0
    return;
46
0
  }
47
48
0
  fprintf(stderr, "[FQ-DIAG] %s count=%d\n", pTag ? pTag : "(null)",
49
0
          pExprs ? LIST_LENGTH((SNodeList*)pExprs) : 0);
50
0
  if (NULL == pExprs) {
51
0
    return;
52
0
  }
53
54
0
  int32_t idx = 0;
55
0
  SNode* pExpr = NULL;
56
0
  FOREACH(pExpr, pExprs) {
57
0
    const SNode* pInner = pExpr;
58
0
    if (QUERY_NODE_TARGET == nodeType((SNode*)pInner)) {
59
0
      pInner = ((const STargetNode*)pInner)->pExpr;
60
0
    }
61
62
0
    const char* pAlias = nodesIsExprNode((SNode*)pInner) ? ((const SExprNode*)pInner)->aliasName : "";
63
0
    const char* pUserAlias = nodesIsExprNode((SNode*)pInner) ? ((const SExprNode*)pInner)->userAlias : "";
64
0
    const char* pColName = (NULL != pInner && QUERY_NODE_COLUMN == nodeType((SNode*)pInner))
65
0
                               ? ((const SColumnNode*)pInner)->colName
66
0
                               : "";
67
0
    const char* pFuncName = (NULL != pInner && QUERY_NODE_FUNCTION == nodeType((SNode*)pInner))
68
0
                                ? ((const SFunctionNode*)pInner)->functionName
69
0
                                : "";
70
0
    fprintf(stderr,
71
0
            "[FQ-DIAG]   [%d] rawType=%d innerType=%d alias=%s userAlias=%s col=%s func=%s\n",
72
0
            idx, pExpr ? (int)nodeType(pExpr) : -1, pInner ? (int)nodeType((SNode*)pInner) : -1,
73
0
            pAlias, pUserAlias, pColName, pFuncName);
74
0
    ++idx;
75
0
  }
76
0
}
77
78
typedef struct SSlotIndex {
79
  int64_t dataBlockId;
80
  SArray*  pSlotIdsInfo;  // duplicate name slot
81
} SSlotIndex;
82
83
enum {
84
  SLOT_KEY_TYPE_ALL = 1,
85
  SLOT_KEY_TYPE_COLNAME = 2,
86
  SLOT_KEY_TYPE_REF = 3,
87
};
88
89
0
static bool fqRemotePlanHasRepeatScanAgg(const SLogicNode* pNode) {
90
0
  if (NULL == pNode) {
91
0
    return false;
92
0
  }
93
94
0
  if (QUERY_NODE_LOGIC_PLAN_AGG == nodeType(pNode)) {
95
0
    const SAggLogicNode* pAgg = (const SAggLogicNode*)pNode;
96
0
    const SNode*         pFunc = NULL;
97
0
    FOREACH(pFunc, pAgg->pAggFuncs) {
98
0
      if (QUERY_NODE_FUNCTION == nodeType(pFunc) &&
99
0
          fmIsRepeatScanFunc(((const SFunctionNode*)pFunc)->funcId)) {
100
0
        return true;
101
0
      }
102
0
    }
103
0
  }
104
105
0
  if (NULL == pNode->pChildren) {
106
0
    return false;
107
0
  }
108
109
0
  const SNode* pChild = NULL;
110
0
  FOREACH(pChild, pNode->pChildren) {
111
0
    if (fqRemotePlanHasRepeatScanAgg((const SLogicNode*)pChild)) {
112
0
      return true;
113
0
    }
114
0
  }
115
116
0
  return false;
117
0
}
118
119
0
static bool fqExprHasAnalyzeInfluxScalarAgg(const SNode* pNode, bool underAgg) {
120
0
  if (NULL == pNode) {
121
0
    return false;
122
0
  }
123
124
0
  switch (nodeType(pNode)) {
125
0
    case QUERY_NODE_TARGET:
126
0
      return fqExprHasAnalyzeInfluxScalarAgg((const SNode*)((const STargetNode*)pNode)->pExpr, underAgg);
127
0
    case QUERY_NODE_REMOTE_VALUE:
128
0
      return underAgg;
129
0
    case QUERY_NODE_OPERATOR: {
130
0
      const SOperatorNode* pOp = (const SOperatorNode*)pNode;
131
0
      return fqExprHasAnalyzeInfluxScalarAgg(pOp->pLeft, underAgg) ||
132
0
             fqExprHasAnalyzeInfluxScalarAgg(pOp->pRight, underAgg);
133
0
    }
134
0
    case QUERY_NODE_LOGIC_CONDITION: {
135
0
      const SLogicConditionNode* pCond = (const SLogicConditionNode*)pNode;
136
0
      const SNode*               pChild = NULL;
137
0
      FOREACH(pChild, pCond->pParameterList) {
138
0
        if (fqExprHasAnalyzeInfluxScalarAgg(pChild, underAgg)) {
139
0
          return true;
140
0
        }
141
0
      }
142
0
      return false;
143
0
    }
144
0
    case QUERY_NODE_CASE_WHEN: {
145
0
      const SCaseWhenNode* pCase = (const SCaseWhenNode*)pNode;
146
0
      if (fqExprHasAnalyzeInfluxScalarAgg(pCase->pCase, underAgg) ||
147
0
          fqExprHasAnalyzeInfluxScalarAgg(pCase->pElse, underAgg)) {
148
0
        return true;
149
0
      }
150
151
0
      const SNode* pWhenThen = NULL;
152
0
      FOREACH(pWhenThen, pCase->pWhenThenList) {
153
0
        const SWhenThenNode* pItem = (const SWhenThenNode*)pWhenThen;
154
0
        if (fqExprHasAnalyzeInfluxScalarAgg(pItem->pWhen, underAgg) ||
155
0
            fqExprHasAnalyzeInfluxScalarAgg(pItem->pThen, underAgg)) {
156
0
          return true;
157
0
        }
158
0
      }
159
0
      return false;
160
0
    }
161
0
    case QUERY_NODE_FUNCTION: {
162
0
      const SFunctionNode* pFunc = (const SFunctionNode*)pNode;
163
0
      bool                 nextUnderAgg = underAgg || fmIsAggFunc(pFunc->funcId);
164
0
      const SNode*         pArg = NULL;
165
0
      FOREACH(pArg, pFunc->pParameterList) {
166
0
        if (fqExprHasAnalyzeInfluxScalarAgg(pArg, nextUnderAgg)) {
167
0
          return true;
168
0
        }
169
0
      }
170
0
      return false;
171
0
    }
172
0
    case QUERY_NODE_NODE_LIST: {
173
0
      const SNodeListNode* pList = (const SNodeListNode*)pNode;
174
0
      const SNode*         pItem = NULL;
175
0
      FOREACH(pItem, pList->pNodeList) {
176
0
        if (fqExprHasAnalyzeInfluxScalarAgg(pItem, underAgg)) {
177
0
          return true;
178
0
        }
179
0
      }
180
0
      return false;
181
0
    }
182
0
    default:
183
0
      return false;
184
0
  }
185
0
}
186
187
0
static bool fqRemoteLogicNeedsAnalyzeInfluxScalarAggFallback(const SLogicNode* pNode) {
188
0
  if (NULL == pNode) {
189
0
    return false;
190
0
  }
191
192
0
  if (fqExprHasAnalyzeInfluxScalarAgg((const SNode*)pNode->pConditions, false)) {
193
0
    return true;
194
0
  }
195
196
0
  const SNode* pTarget = NULL;
197
0
  FOREACH(pTarget, pNode->pTargets) {
198
0
    if (fqExprHasAnalyzeInfluxScalarAgg(pTarget, false)) {
199
0
      return true;
200
0
    }
201
0
  }
202
203
0
  switch (nodeType((const SNode*)pNode)) {
204
0
    case QUERY_NODE_LOGIC_PLAN_AGG: {
205
0
      const SAggLogicNode* pAgg = (const SAggLogicNode*)pNode;
206
0
      const SNode*         pExpr = NULL;
207
0
      FOREACH(pExpr, pAgg->pAggFuncs) {
208
0
        if (fqExprHasAnalyzeInfluxScalarAgg(pExpr, false)) {
209
0
          return true;
210
0
        }
211
0
      }
212
0
      FOREACH(pExpr, pAgg->pGroupKeys) {
213
0
        if (fqExprHasAnalyzeInfluxScalarAgg(pExpr, false)) {
214
0
          return true;
215
0
        }
216
0
      }
217
0
      break;
218
0
    }
219
0
    case QUERY_NODE_LOGIC_PLAN_PROJECT: {
220
0
      const SProjectLogicNode* pProj = (const SProjectLogicNode*)pNode;
221
0
      const SNode*             pExpr = NULL;
222
0
      FOREACH(pExpr, pProj->pProjections) {
223
0
        if (fqExprHasAnalyzeInfluxScalarAgg(pExpr, false)) {
224
0
          return true;
225
0
        }
226
0
      }
227
0
      break;
228
0
    }
229
0
    default:
230
0
      break;
231
0
  }
232
233
0
  const SNode* pChild = NULL;
234
0
  FOREACH(pChild, pNode->pChildren) {
235
0
    if (fqRemoteLogicNeedsAnalyzeInfluxScalarAggFallback((const SLogicNode*)pChild)) {
236
0
      return true;
237
0
    }
238
0
  }
239
240
0
  return false;
241
0
}
242
243
static bool fqShouldUseAnalyzeInfluxScalarAggFallback(const SPhysiPlanContext* pCxt,
244
0
                                                      const SScanLogicNode* pScanLogicNode) {
245
0
  if (NULL == pCxt || NULL == pCxt->pPlanCxt || NULL == pScanLogicNode ||
246
0
      NULL == pScanLogicNode->pExtTableNode || NULL == pScanLogicNode->pRemoteLogicPlan) {
247
0
    return false;
248
0
  }
249
250
0
  if (((const SExtTableNode*)pScanLogicNode->pExtTableNode)->sourceType != EXT_SOURCE_INFLUXDB) {
251
0
    return false;
252
0
  }
253
254
0
  if (QUERY_NODE_EXPLAIN_STMT != nodeType(pCxt->pPlanCxt->pAstRoot)) {
255
0
    return false;
256
0
  }
257
258
0
  if (!((const SExplainStmt*)pCxt->pPlanCxt->pAstRoot)->analyze) {
259
0
    return false;
260
0
  }
261
262
0
  return fqRemoteLogicNeedsAnalyzeInfluxScalarAggFallback((const SLogicNode*)pScanLogicNode->pRemoteLogicPlan);
263
0
}
264
265
static int32_t addDataBlockSlotsImpl(SPhysiPlanContext* pCxt, SNodeList* pList, SDataBlockDescNode* pDataBlockDesc,
266
                                     const char* pStmtName, bool output, bool reserve);
267
268
static int32_t getSlotKeyHelper(SNode* pNode, const char* pPreName, const char* name, char** ppKey, int32_t callocLen,
269
0
                                int32_t* pLen, uint16_t extraBufLen, int8_t slotKeyType) {
270
0
  int32_t code = 0;
271
0
  *ppKey = taosMemoryCalloc(1, callocLen);
272
0
  if (!*ppKey) {
273
0
    return terrno;
274
0
  }
275
0
  if (slotKeyType == SLOT_KEY_TYPE_ALL) {
276
0
    TAOS_STRNCAT(*ppKey, pPreName, TSDB_TABLE_NAME_LEN);
277
0
    TAOS_STRNCAT(*ppKey, ".", 2);
278
0
    TAOS_STRNCAT(*ppKey, name, TSDB_COL_NAME_LEN);
279
0
    *pLen = taosHashBinary(*ppKey, strlen(*ppKey), callocLen);
280
0
  } else {
281
0
    TAOS_STRNCAT(*ppKey, name, TSDB_COL_NAME_LEN);
282
0
    *pLen = strlen(*ppKey);
283
0
  }
284
0
  return code;
285
0
}
286
287
static int32_t getSlotKey(SNode* pNode, const char* pStmtName, char** ppKey, int32_t* pLen, uint16_t extraBufLen,
288
0
                          int32_t* cap) {
289
0
  int32_t code = 0;
290
0
  int32_t callocLen = 0;
291
0
  if (QUERY_NODE_COLUMN == nodeType(pNode)) {
292
0
    SColumnNode* pCol = (SColumnNode*)pNode;
293
0
    if (NULL != pStmtName) {
294
0
      if ('\0' != pStmtName[0]) {
295
0
        callocLen = TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen;
296
0
        *cap = callocLen;
297
0
        return getSlotKeyHelper(pNode, pStmtName, pCol->node.aliasName, ppKey, callocLen, pLen, extraBufLen,
298
0
                                SLOT_KEY_TYPE_ALL);
299
0
      } else {
300
0
        callocLen = TSDB_COL_NAME_LEN + 1 + extraBufLen;
301
0
        *cap = callocLen;
302
0
        return getSlotKeyHelper(pNode, pStmtName, pCol->node.aliasName, ppKey, callocLen, pLen, extraBufLen,
303
0
                                SLOT_KEY_TYPE_COLNAME);
304
0
      }
305
0
    }
306
0
    if ('\0' == pCol->tableAlias[0]) {
307
0
      callocLen = TSDB_COL_NAME_LEN + 1 + extraBufLen;
308
0
      *cap = callocLen;
309
0
      return getSlotKeyHelper(pNode, pStmtName, pCol->colName, ppKey, callocLen, pLen, extraBufLen,
310
0
                              SLOT_KEY_TYPE_COLNAME);
311
0
    }
312
0
    if (pCol->hasRef) {
313
0
      int32_t allocLen = TSDB_EXT_SOURCE_NAME_LEN + TSDB_TABLE_FNAME_LEN + TSDB_COL_NAME_LEN * 2 + 5 + extraBufLen;
314
0
      *ppKey = taosMemoryCalloc(1, allocLen);
315
0
      if (!*ppKey) {
316
0
        return terrno;
317
0
      }
318
      // For tag columns, include colName to prevent slot collisions when STB-level
319
      // metadata non-deterministically reports the same ref source for different tags
320
0
      if (pCol->colType == COLUMN_TYPE_TAG) {
321
0
        TAOS_STRNCAT(*ppKey, pCol->colName, TSDB_COL_NAME_LEN);
322
0
        TAOS_STRNCAT(*ppKey, ":", 2);
323
0
      }
324
0
      if ('\0' != pCol->refSourceName[0]) {
325
0
        TAOS_STRNCAT(*ppKey, pCol->refSourceName, TSDB_EXT_SOURCE_NAME_LEN);
326
0
        TAOS_STRNCAT(*ppKey, ".", 2);
327
0
      }
328
0
      TAOS_STRNCAT(*ppKey, pCol->refDbName, TSDB_DB_NAME_LEN);
329
0
      TAOS_STRNCAT(*ppKey, ".", 2);
330
0
      TAOS_STRNCAT(*ppKey, pCol->refTableName, TSDB_TABLE_NAME_LEN);
331
0
      TAOS_STRNCAT(*ppKey, ".", 2);
332
0
      TAOS_STRNCAT(*ppKey, pCol->refColName, TSDB_COL_NAME_LEN);
333
0
      *pLen = taosHashBinary(*ppKey, strlen(*ppKey), allocLen);
334
0
      *cap = allocLen;
335
0
      return code;
336
0
    }
337
0
    if (pCol->hasDep) {
338
0
      int32_t allocLen = TSDB_EXT_SOURCE_NAME_LEN + TSDB_TABLE_FNAME_LEN + TSDB_COL_NAME_LEN + 4 + extraBufLen;
339
0
      *ppKey = taosMemoryCalloc(1, allocLen);
340
0
      if (!*ppKey) {
341
0
        return terrno;
342
0
      }
343
0
      if ('\0' != pCol->refSourceName[0]) {
344
0
        TAOS_STRNCAT(*ppKey, pCol->refSourceName, TSDB_EXT_SOURCE_NAME_LEN);
345
0
        TAOS_STRNCAT(*ppKey, ".", 2);
346
0
      }
347
0
      TAOS_STRNCAT(*ppKey, pCol->dbName, TSDB_DB_NAME_LEN);
348
0
      TAOS_STRNCAT(*ppKey, ".", 2);
349
0
      TAOS_STRNCAT(*ppKey, pCol->tableAlias, TSDB_TABLE_NAME_LEN);
350
0
      TAOS_STRNCAT(*ppKey, ".", 2);
351
0
      TAOS_STRNCAT(*ppKey, pCol->colName, TSDB_COL_NAME_LEN);
352
0
      planDebug("getSlotKey hasDep: '%s' (len=%d)", *ppKey, (int)strlen(*ppKey));
353
0
      *cap = allocLen;
354
0
      *pLen = taosHashBinary(*ppKey, strlen(*ppKey), allocLen);
355
0
      return code;
356
0
    }
357
0
    callocLen = TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen;
358
0
    *cap = callocLen;
359
0
    planDebug("getSlotKey default: tableAlias='%s' colName='%s' hasRef=%d hasDep=%d", pCol->tableAlias, pCol->colName, pCol->hasRef, pCol->hasDep);
360
0
    return getSlotKeyHelper(pNode, pCol->tableAlias, pCol->colName, ppKey, callocLen, pLen, extraBufLen,
361
0
                            SLOT_KEY_TYPE_ALL);
362
0
  } else if (QUERY_NODE_FUNCTION == nodeType(pNode)) {
363
0
    SFunctionNode* pFunc = (SFunctionNode*)pNode;
364
0
    if (FUNCTION_TYPE_TBNAME == pFunc->funcType) {
365
0
      SValueNode* pVal = (SValueNode*)nodesListGetNode(pFunc->pParameterList, 0);
366
0
      if (pVal) {
367
0
        if (NULL != pStmtName && '\0' != pStmtName[0]) {
368
0
          callocLen = TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen;
369
0
          return getSlotKeyHelper(pNode, pStmtName, ((SExprNode*)pNode)->aliasName, ppKey, callocLen, pLen, extraBufLen,
370
0
                                  SLOT_KEY_TYPE_ALL);
371
0
        }
372
0
        int32_t literalLen = strlen(pVal->literal);
373
0
        callocLen = literalLen + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen;
374
0
        *cap = callocLen;
375
0
        return getSlotKeyHelper(pNode, pVal->literal, ((SExprNode*)pNode)->aliasName, ppKey, callocLen, pLen,
376
0
                                extraBufLen, SLOT_KEY_TYPE_ALL);
377
0
      }
378
0
    }
379
0
  }
380
381
0
  if (NULL != pStmtName && '\0' != pStmtName[0]) {
382
0
    callocLen = TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen;
383
0
    *cap = callocLen;
384
0
    return getSlotKeyHelper(pNode, pStmtName, ((SExprNode*)pNode)->aliasName, ppKey, callocLen, pLen, extraBufLen,
385
0
                            SLOT_KEY_TYPE_ALL);
386
0
  }
387
388
0
  callocLen = TSDB_COL_NAME_LEN + 1 + extraBufLen;
389
0
  *cap = callocLen;
390
0
  code = getSlotKeyHelper(pNode, pStmtName, ((SExprNode*)pNode)->aliasName, ppKey, callocLen, pLen, extraBufLen,
391
0
                          SLOT_KEY_TYPE_COLNAME);
392
0
  if (TSDB_CODE_SUCCESS == code) {
393
0
    qDebug("FQ-Slots: getSlotKey exprType=%d stmtName=%s aliasName=%s key=%s len=%d cap=%d",
394
0
           (int)nodeType(pNode), (NULL != pStmtName ? pStmtName : "(null)"),
395
0
           ((SExprNode*)pNode)->aliasName, (NULL != *ppKey ? *ppKey : "(null)"), *pLen, *cap);
396
0
  } else {
397
0
    planError("FQ-Slots: getSlotKey failed exprType=%d stmtName=%s aliasName=%s code=0x%x",
398
0
              (int)nodeType(pNode), (NULL != pStmtName ? pStmtName : "(null)"),
399
0
              ((SExprNode*)pNode)->aliasName, code);
400
0
  }
401
0
  return code;
402
0
  return code;
403
0
}
404
405
static SNode* createSlotDesc(SPhysiPlanContext* pCxt, const char* pName, const SNode* pNode, int16_t slotId,
406
0
                             bool output, bool reserve) {
407
0
  SSlotDescNode* pSlot = NULL;
408
0
  int32_t        code = nodesMakeNode(QUERY_NODE_SLOT_DESC, (SNode**)&pSlot);
409
0
  if (NULL == pSlot) {
410
0
    terrno = code;
411
0
    return NULL;
412
0
  }
413
0
  snprintf(pSlot->name, sizeof(pSlot->name), "%s", pName);
414
0
  pSlot->slotId = slotId;
415
0
  pSlot->dataType = ((SExprNode*)pNode)->resType;
416
0
  pSlot->reserve = reserve;
417
0
  pSlot->output = output;
418
0
  return (SNode*)pSlot;
419
0
}
420
421
0
static int32_t createTarget(SNode* pNode, int64_t dataBlockId, int16_t slotId, SNode** pOutput) {
422
0
  STargetNode* pTarget = NULL;
423
0
  int32_t      code = nodesMakeNode(QUERY_NODE_TARGET, (SNode**)&pTarget);
424
0
  if (NULL == pTarget) {
425
0
    return code;
426
0
  }
427
428
0
  pTarget->dataBlockId = dataBlockId;
429
0
  pTarget->slotId = slotId;
430
0
  pTarget->pExpr = pNode;
431
432
0
  *pOutput = (SNode*)pTarget;
433
0
  return TSDB_CODE_SUCCESS;
434
0
}
435
436
0
static int32_t putSlotToHashImpl(int64_t dataBlockId, int16_t slotId, const char* pName, int32_t len, SHashObj* pHash) {
437
0
  SSlotIndex* pIndex = taosHashGet(pHash, pName, len);
438
0
  if (NULL != pIndex) {
439
0
    SSlotIdInfo info = {.slotId = slotId, .set = false};
440
0
    if (NULL == taosArrayPush(pIndex->pSlotIdsInfo, &info)) {
441
0
      return terrno;
442
0
    }
443
0
    return TSDB_CODE_SUCCESS;
444
0
  }
445
446
0
  SSlotIndex index = {.dataBlockId = dataBlockId, .pSlotIdsInfo = taosArrayInit(TARRAY_MIN_SIZE, sizeof(SSlotIdInfo))};
447
0
  if (NULL == index.pSlotIdsInfo) {
448
0
    return terrno;
449
0
  }
450
0
  SSlotIdInfo info = {.slotId = slotId, .set = false};
451
0
  if (NULL == taosArrayPush(index.pSlotIdsInfo, &info)) {
452
0
    taosArrayDestroy(index.pSlotIdsInfo);
453
0
    return terrno;
454
0
  }
455
0
  int32_t code = taosHashPut(pHash, pName, len, &index, sizeof(SSlotIndex));
456
0
  if (TSDB_CODE_SUCCESS != code) {
457
0
    taosArrayDestroy(index.pSlotIdsInfo);
458
0
  }
459
0
  return code;
460
0
}
461
462
static int32_t putSlotToHash(const char* pName, int32_t len, int64_t dataBlockId, int16_t slotId, SNode* pNode,
463
0
                             SHashObj* pHash) {
464
0
  return putSlotToHashImpl(dataBlockId, slotId, pName, len, pHash);
465
0
}
466
467
static int32_t createDataBlockDescHash(SPhysiPlanContext* pCxt, int32_t capacity, int64_t dataBlockId,
468
0
                                       SHashObj** pDescHash, SHashObj** ppProjIdxDescHash) {
469
0
  SHashObj* pHash = taosHashInit(capacity, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), false, HASH_NO_LOCK);
470
0
  if (NULL == pHash) {
471
0
    return terrno;
472
0
  }
473
0
  SHashObj* pProjIdxHash = taosHashInit(capacity, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_NO_LOCK);
474
0
  if (!pProjIdxHash) {
475
0
    taosHashCleanup(pHash);
476
0
    return terrno;
477
0
  }
478
0
  if (NULL == taosArrayInsert(pCxt->pLocationHelper, (size_t)dataBlockId, &pHash)) {
479
0
    taosHashCleanup(pHash);
480
0
    taosHashCleanup(pProjIdxHash);
481
0
    return terrno;
482
0
  }
483
0
  if (NULL == taosArrayInsert(pCxt->pProjIdxLocHelper, (size_t)dataBlockId, &pProjIdxHash)) {
484
0
    taosHashCleanup(pHash);
485
0
    taosHashCleanup(pProjIdxHash);
486
0
    return terrno;
487
0
  }
488
489
0
  *pDescHash = pHash;
490
0
  *ppProjIdxDescHash = pProjIdxHash;
491
0
  return TSDB_CODE_SUCCESS;
492
0
}
493
494
0
static void fqResetLocationHashSlotSet(SHashObj* pHash) {
495
0
  if (NULL == pHash) {
496
0
    return;
497
0
  }
498
0
499
0
  SSlotIndex* pIndex = taosHashIterate(pHash, NULL);
500
0
  while (NULL != pIndex) {
501
0
    if (NULL != pIndex->pSlotIdsInfo) {
502
0
      int32_t size = taosArrayGetSize(pIndex->pSlotIdsInfo);
503
0
      for (int32_t i = 0; i < size; ++i) {
504
0
        SSlotIdInfo* pInfo = taosArrayGet(pIndex->pSlotIdsInfo, i);
505
0
        if (NULL != pInfo) {
506
0
          pInfo->set = false;
507
0
        }
508
0
      }
509
0
    }
510
0
    pIndex = taosHashIterate(pHash, pIndex);
511
0
  }
512
0
}
513
514
static int32_t buildDataBlockSlots(SPhysiPlanContext* pCxt, SNodeList* pList, SDataBlockDescNode* pDataBlockDesc,
515
0
                                   SHashObj* pHash, SHashObj* pProjIdxDescHash) {
516
0
  pDataBlockDesc->pSlots = NULL;
517
0
  int32_t code = nodesMakeList(&pDataBlockDesc->pSlots);
518
0
  if (NULL == pDataBlockDesc->pSlots) {
519
0
    return code;
520
0
  }
521
522
0
  int16_t slotId = 0;
523
0
  SNode*  pNode = NULL;
524
0
  FOREACH(pNode, pList) {
525
0
    char*   name = NULL;
526
0
    int32_t len = 0;
527
0
    int32_t cap = 0;
528
0
    code = getSlotKey(pNode, NULL, &name, &len, 16, &cap);
529
0
    planDebug("FQ-Slots: buildDataBlockSlots dataBlockId=%" PRId64 " slotId=%d exprNodeType=%d key=%s len=%d code=0x%x",
530
0
              pDataBlockDesc->dataBlockId, slotId, (int)nodeType(pNode),
531
0
              (NULL != name ? name : "(null)"), len, code);
532
0
    if (TSDB_CODE_SUCCESS == code) {
533
0
      code = nodesListStrictAppend(pDataBlockDesc->pSlots, createSlotDesc(pCxt, name, pNode, slotId, true, false));
534
0
    }
535
0
    code = putSlotToHash(name, len, pDataBlockDesc->dataBlockId, slotId, pNode, pHash);
536
0
    if (TSDB_CODE_SUCCESS == code) {
537
0
      if (nodeType(pNode) == QUERY_NODE_COLUMN && ((SColumnNode*)pNode)->resIdx > 0) {
538
0
        snprintf(name + strlen(name), cap, "_%d", ((SColumnNode*)pNode)->resIdx);
539
0
        code = putSlotToHash(name, strlen(name), pDataBlockDesc->dataBlockId, slotId, pNode, pProjIdxDescHash);
540
0
      }
541
0
    }
542
0
    taosMemoryFree(name);
543
0
    if (TSDB_CODE_SUCCESS == code) {
544
0
      pDataBlockDesc->totalRowSize += ((SExprNode*)pNode)->resType.bytes;
545
0
      pDataBlockDesc->outputRowSize += ((SExprNode*)pNode)->resType.bytes;
546
0
      ++slotId;
547
0
    } else {
548
0
      break;
549
0
    }
550
0
  }
551
0
  return code;
552
0
}
553
554
0
static int32_t createDataBlockDesc(SPhysiPlanContext* pCxt, SNodeList* pList, SDataBlockDescNode** pDataBlockDesc) {
555
0
  SDataBlockDescNode* pDesc = NULL;
556
0
  int32_t             code = nodesMakeNode(QUERY_NODE_DATABLOCK_DESC, (SNode**)&pDesc);
557
0
  if (NULL == pDesc) {
558
0
    return code;
559
0
  }
560
0
  pDesc->dataBlockId = pCxt->nextDataBlockId++;
561
0
  planDebug("FQ-Slots: createDataBlockDesc start dataBlockId=%" PRId64 " targets=%d",
562
0
            pDesc->dataBlockId, pList ? LIST_LENGTH(pList) : 0);
563
564
0
  SHashObj* pHash = NULL;
565
0
  SHashObj* pProjIdxHash = NULL;
566
0
  code = createDataBlockDescHash(pCxt, LIST_LENGTH(pList), pDesc->dataBlockId, &pHash, &pProjIdxHash);
567
0
  if (TSDB_CODE_SUCCESS == code) {
568
0
    code = buildDataBlockSlots(pCxt, pList, pDesc, pHash, pProjIdxHash);
569
0
  }
570
571
0
  if (TSDB_CODE_SUCCESS == code) {
572
0
    *pDataBlockDesc = pDesc;
573
0
    planDebug("FQ-Slots: createDataBlockDesc done dataBlockId=%" PRId64 " slots=%d totalRowSize=%d outputRowSize=%d",
574
0
              pDesc->dataBlockId, pDesc->pSlots ? LIST_LENGTH(pDesc->pSlots) : 0,
575
0
              pDesc->totalRowSize, pDesc->outputRowSize);
576
0
  } else {
577
0
    planError("FQ-Slots: createDataBlockDesc failed dataBlockId=%" PRId64 " targets=%d code=0x%x",
578
0
              pDesc->dataBlockId, pList ? LIST_LENGTH(pList) : 0, code);
579
0
    nodesDestroyNode((SNode*)pDesc);
580
0
  }
581
582
0
  return code;
583
0
}
584
585
0
static void destroySlotIndexHash(SHashObj* pHash) {
586
0
  if (NULL == pHash) {
587
0
    return;
588
0
  }
589
590
0
  SSlotIndex* pIndex = taosHashIterate(pHash, NULL);
591
0
  while (NULL != pIndex) {
592
0
    taosArrayDestroy(pIndex->pSlotIdsInfo);
593
0
    pIndex = taosHashIterate(pHash, pIndex);
594
0
  }
595
0
  taosHashCleanup(pHash);
596
0
}
597
598
0
static void fqClearDataBlockDescHashes(SPhysiPlanContext* pCxt, int64_t dataBlockId) {
599
0
  if (NULL == pCxt) {
600
0
    return;
601
0
  }
602
603
0
  if (NULL != pCxt->pLocationHelper && dataBlockId < taosArrayGetSize(pCxt->pLocationHelper)) {
604
0
    SHashObj* pHash = taosArrayGetP(pCxt->pLocationHelper, (size_t)dataBlockId);
605
0
    if (NULL != pHash) {
606
0
      SHashObj* pNull = NULL;
607
0
      destroySlotIndexHash(pHash);
608
0
      taosArraySet(pCxt->pLocationHelper, (size_t)dataBlockId, &pNull);
609
0
    }
610
0
  }
611
612
0
  if (NULL != pCxt->pProjIdxLocHelper && dataBlockId < taosArrayGetSize(pCxt->pProjIdxLocHelper)) {
613
0
    SHashObj* pHash = taosArrayGetP(pCxt->pProjIdxLocHelper, (size_t)dataBlockId);
614
0
    if (NULL != pHash) {
615
0
      SHashObj* pNull = NULL;
616
0
      destroySlotIndexHash(pHash);
617
0
      taosArraySet(pCxt->pProjIdxLocHelper, (size_t)dataBlockId, &pNull);
618
0
    }
619
0
  }
620
0
}
621
622
static int32_t fqRebuildDataBlockDesc(SPhysiPlanContext* pCxt, SDataBlockDescNode* pDesc, SNodeList* pOutputCols,
623
0
                                      SNodeList* pReserveCols) {
624
0
  if (NULL == pCxt || NULL == pDesc) {
625
0
    return TSDB_CODE_INVALID_PARA;
626
0
  }
627
628
0
  int64_t dataBlockId = pDesc->dataBlockId;
629
0
  planDebug("FQ-Slots: fqRebuildDataBlockDesc start dataBlockId=%" PRId64 " outputCols=%d reserveCols=%d",
630
0
            dataBlockId, pOutputCols ? LIST_LENGTH(pOutputCols) : 0,
631
0
            pReserveCols ? LIST_LENGTH(pReserveCols) : 0);
632
0
  int32_t cap = 0;
633
0
  if (pOutputCols != NULL) {
634
0
    cap += LIST_LENGTH(pOutputCols);
635
0
  }
636
0
  if (pReserveCols != NULL) {
637
0
    cap += LIST_LENGTH(pReserveCols);
638
0
  }
639
0
  if (cap <= 0) {
640
0
    cap = 1;
641
0
  }
642
643
0
  nodesDestroyList(pDesc->pSlots);
644
0
  pDesc->pSlots = NULL;
645
0
  pDesc->totalRowSize = 0;
646
0
  pDesc->outputRowSize = 0;
647
648
0
  fqClearDataBlockDescHashes(pCxt, dataBlockId);
649
650
0
  SHashObj* pHash = NULL;
651
0
  SHashObj* pProjIdxHash = NULL;
652
0
  int32_t code = createDataBlockDescHash(pCxt, cap, dataBlockId, &pHash, &pProjIdxHash);
653
0
  if (TSDB_CODE_SUCCESS != code) {
654
0
    return code;
655
0
  }
656
657
0
  code = nodesMakeList(&pDesc->pSlots);
658
0
  if (TSDB_CODE_SUCCESS != code) {
659
0
    fqClearDataBlockDescHashes(pCxt, dataBlockId);
660
0
    return code;
661
0
  }
662
663
0
  if (pOutputCols != NULL) {
664
0
    code = addDataBlockSlotsImpl(pCxt, pOutputCols, pDesc, NULL, true, false);
665
0
    if (TSDB_CODE_SUCCESS != code) {
666
0
      return code;
667
0
    }
668
0
  }
669
670
0
  if (pReserveCols != NULL) {
671
0
    code = addDataBlockSlotsImpl(pCxt, pReserveCols, pDesc, NULL, false, true);
672
0
    if (TSDB_CODE_SUCCESS != code) {
673
0
      return code;
674
0
    }
675
0
  }
676
677
0
  planDebug("FQ-Slots: rebuilt dataBlockDesc=%" PRId64 " outputCols=%d reserveCols=%d totalSlots=%d totalRowSize=%d outputRowSize=%d",
678
0
            dataBlockId,
679
0
            pOutputCols ? LIST_LENGTH(pOutputCols) : 0,
680
0
            pReserveCols ? LIST_LENGTH(pReserveCols) : 0,
681
0
            pDesc->pSlots ? LIST_LENGTH(pDesc->pSlots) : 0,
682
0
            pDesc->totalRowSize, pDesc->outputRowSize);
683
0
  return TSDB_CODE_SUCCESS;
684
0
}
685
686
0
static int16_t getUnsetSlotId(const SArray* pSlotIdsInfo) {
687
0
  int32_t size = taosArrayGetSize(pSlotIdsInfo);
688
0
  for (int32_t i = 0; i < size; ++i) {
689
0
    SSlotIdInfo* pInfo = taosArrayGet(pSlotIdsInfo, i);
690
0
    if (!pInfo->set) {
691
0
      pInfo->set = true;
692
0
      return pInfo->slotId;
693
0
    }
694
0
  }
695
0
  return ((SSlotIdInfo*)taosArrayGet(pSlotIdsInfo, 0))->slotId;
696
0
}
697
698
static int32_t addDataBlockSlotsImpl(SPhysiPlanContext* pCxt, SNodeList* pList, SDataBlockDescNode* pDataBlockDesc,
699
0
                                     const char* pStmtName, bool output, bool reserve) {
700
0
  if (NULL == pList) {
701
0
    return TSDB_CODE_SUCCESS;
702
0
  }
703
704
0
  int32_t   code = TSDB_CODE_SUCCESS;
705
0
  SHashObj* pHash = taosArrayGetP(pCxt->pLocationHelper, pDataBlockDesc->dataBlockId);
706
0
  int16_t   nextSlotId = LIST_LENGTH(pDataBlockDesc->pSlots), slotId = 0;
707
0
  SNode*    pNode = NULL;
708
0
  FOREACH(pNode, pList) {
709
0
    SNode*  pExpr = QUERY_NODE_ORDER_BY_EXPR == nodeType(pNode) ? ((SOrderByExprNode*)pNode)->pExpr : pNode;
710
0
    char*   name = NULL;
711
0
    int32_t len = 0;
712
0
    int32_t cap = 0;
713
0
    code = getSlotKey(pExpr, pStmtName, &name, &len, 0, &cap);
714
0
    planDebug("FQ-Slots: addDataBlockSlots dataBlockId=%" PRId64 " nextSlotId=%d output=%d reserve=%d exprNodeType=%d exprAlias=%s key=%s len=%d code=0x%x",
715
0
              pDataBlockDesc->dataBlockId, nextSlotId, (int)output, (int)reserve,
716
0
              (int)nodeType(pExpr), ((SExprNode*)pExpr)->aliasName,
717
0
              (NULL != name ? name : "(null)"), len, code);
718
0
    if (TSDB_CODE_SUCCESS == code) {
719
0
      SSlotIndex* pIndex = taosHashGet(pHash, name, len);
720
0
      if (NULL == pIndex) {
721
0
        code = nodesListStrictAppend(pDataBlockDesc->pSlots,
722
0
                                     createSlotDesc(pCxt, name, pExpr, nextSlotId, output, reserve));
723
0
        if (TSDB_CODE_SUCCESS == code) {
724
0
          code = putSlotToHashImpl(pDataBlockDesc->dataBlockId, nextSlotId, name, len, pHash);
725
0
        }
726
0
        pDataBlockDesc->totalRowSize += ((SExprNode*)pExpr)->resType.bytes;
727
0
        if (output) {
728
0
          pDataBlockDesc->outputRowSize += ((SExprNode*)pExpr)->resType.bytes;
729
0
        }
730
0
        slotId = nextSlotId;
731
0
        ++nextSlotId;
732
0
      } else {
733
0
        slotId = getUnsetSlotId(pIndex->pSlotIdsInfo);
734
0
      }
735
0
    }
736
737
0
    taosMemoryFree(name);
738
0
    if (TSDB_CODE_SUCCESS == code) {
739
0
      SNode* pTarget = NULL;
740
0
      code = createTarget(pNode, pDataBlockDesc->dataBlockId, slotId, &pTarget);
741
0
      if (TSDB_CODE_SUCCESS == code) {
742
0
        REPLACE_NODE(pTarget);
743
0
      }
744
0
    }
745
746
0
    if (TSDB_CODE_SUCCESS != code) {
747
0
      break;
748
0
    }
749
0
  }
750
0
  return code;
751
0
}
752
753
0
static int32_t addDataBlockSlots(SPhysiPlanContext* pCxt, SNodeList* pList, SDataBlockDescNode* pDataBlockDesc) {
754
0
  return addDataBlockSlotsImpl(pCxt, pList, pDataBlockDesc, NULL, false, true);
755
0
}
756
757
0
static int32_t addDataBlockSlot(SPhysiPlanContext* pCxt, SNode** pNode, SDataBlockDescNode* pDataBlockDesc) {
758
0
  if (NULL == pNode || NULL == *pNode) {
759
0
    return TSDB_CODE_SUCCESS;
760
0
  }
761
762
0
  SNodeList* pList = NULL;
763
0
  int32_t    code = nodesListMakeAppend(&pList, *pNode);
764
0
  if (TSDB_CODE_SUCCESS == code) {
765
0
    code = addDataBlockSlots(pCxt, pList, pDataBlockDesc);
766
0
  }
767
0
  if (TSDB_CODE_SUCCESS == code) {
768
0
    *pNode = nodesListGetNode(pList, 0);
769
0
  }
770
0
  nodesClearList(pList);
771
0
  return code;
772
0
}
773
774
static int32_t addDataBlockSlotsForProject(SPhysiPlanContext* pCxt, const char* pStmtName, SNodeList* pList,
775
0
                                           SDataBlockDescNode* pDataBlockDesc) {
776
0
  return addDataBlockSlotsImpl(pCxt, pList, pDataBlockDesc, pStmtName, false, true);
777
0
}
778
779
0
static int32_t pushdownDataBlockSlots(SPhysiPlanContext* pCxt, SNodeList* pList, SDataBlockDescNode* pDataBlockDesc) {
780
0
  return addDataBlockSlotsImpl(pCxt, pList, pDataBlockDesc, NULL, true, true);
781
0
}
782
783
typedef struct SSetSlotIdCxt {
784
  int32_t     errCode;
785
  SHashObj*   pLeftHash;
786
  SHashObj*   pLeftProjIdxHash;
787
  SHashObj*   pRightHash;
788
  SHashObj*   pRightProdIdxHash;
789
  const char* pLeftTableAlias;
790
  const char* pRightTableAlias;
791
  const char* pLeftAlias;
792
  const char* pRightAlias;
793
} SSetSlotIdCxt;
794
795
typedef struct SMultiTableSetSlotIdCxt {
796
  int32_t    errCode;
797
  SArray*    hashArray;
798
  SArray*    projIdxHashArray;
799
  SHashObj*  mergedHash;
800
  SHashObj*  mergedProjIdxHash;
801
  SNodeList* pChild;
802
  bool       isVtb;
803
} SMultiTableSetSlotIdCxt;
804
805
static int32_t fqBuildUnqualifiedSlotKey(const char* pName, uint16_t extraBufLen, char** ppKey,
806
0
                                         int32_t* pLen, int32_t* pCap) {
807
0
  if (NULL == pName || '\0' == pName[0] || NULL == ppKey || NULL == pLen || NULL == pCap) {
808
0
    return TSDB_CODE_INVALID_PARA;
809
0
  }
810
811
0
  *ppKey = NULL;
812
0
  *pLen = 0;
813
0
  *pCap = TSDB_COL_NAME_LEN + 1 + extraBufLen;
814
0
  return getSlotKeyHelper(NULL, "", pName, ppKey, *pCap, pLen, extraBufLen, SLOT_KEY_TYPE_COLNAME);
815
0
}
816
817
static int32_t fqBuildQualifiedSlotKey(const char* pTableName, const char* pColName, uint16_t extraBufLen,
818
0
                                       char** ppKey, int32_t* pLen, int32_t* pCap) {
819
0
  if (NULL == pTableName || '\0' == pTableName[0] || NULL == pColName || '\0' == pColName[0] ||
820
0
      NULL == ppKey || NULL == pLen || NULL == pCap) {
821
0
    return TSDB_CODE_INVALID_PARA;
822
0
  }
823
824
0
  *ppKey = NULL;
825
0
  *pLen = 0;
826
0
  *pCap = TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen;
827
0
  return getSlotKeyHelper(NULL, pTableName, pColName, ppKey, *pCap, pLen, extraBufLen, SLOT_KEY_TYPE_ALL);
828
0
}
829
830
static int32_t fqBuildRefSlotKey(const SColumnNode* pCol, uint16_t extraBufLen, char** ppKey, int32_t* pLen,
831
0
                                 int32_t* pCap) {
832
0
  if (NULL == pCol || NULL == ppKey || NULL == pLen || NULL == pCap || !pCol->hasRef ||
833
0
      '\0' == pCol->refDbName[0] || '\0' == pCol->refTableName[0] || '\0' == pCol->refColName[0]) {
834
0
    return TSDB_CODE_INVALID_PARA;
835
0
  }
836
837
0
  int32_t allocLen = TSDB_EXT_SOURCE_NAME_LEN + TSDB_TABLE_FNAME_LEN + TSDB_COL_NAME_LEN * 2 + 5 + extraBufLen;
838
0
  *ppKey = taosMemoryCalloc(1, allocLen);
839
0
  if (NULL == *ppKey) {
840
0
    return terrno;
841
0
  }
842
843
0
  if (COLUMN_TYPE_TAG == pCol->colType) {
844
0
    TAOS_STRNCAT(*ppKey, pCol->colName, TSDB_COL_NAME_LEN);
845
0
    TAOS_STRNCAT(*ppKey, ":", 2);
846
0
  }
847
0
  if ('\0' != pCol->refSourceName[0]) {
848
0
    TAOS_STRNCAT(*ppKey, pCol->refSourceName, TSDB_EXT_SOURCE_NAME_LEN);
849
0
    TAOS_STRNCAT(*ppKey, ".", 2);
850
0
  }
851
0
  TAOS_STRNCAT(*ppKey, pCol->refDbName, TSDB_DB_NAME_LEN);
852
0
  TAOS_STRNCAT(*ppKey, ".", 2);
853
0
  TAOS_STRNCAT(*ppKey, pCol->refTableName, TSDB_TABLE_NAME_LEN);
854
0
  TAOS_STRNCAT(*ppKey, ".", 2);
855
0
  TAOS_STRNCAT(*ppKey, pCol->refColName, TSDB_COL_NAME_LEN);
856
0
  *pLen = taosHashBinary(*ppKey, strlen(*ppKey), allocLen);
857
0
  *pCap = allocLen;
858
0
  return TSDB_CODE_SUCCESS;
859
0
}
860
861
0
static bool fqSlotLookupHasHit(SHashObj* pHash, const char* pKey, int32_t keyLen) {
862
0
  return (NULL != pHash && NULL != pKey && keyLen > 0 && NULL != taosHashGet(pHash, pKey, keyLen));
863
0
}
864
865
0
static void fqAppendSlotDiagItem(char* pBuf, int32_t bufLen, int32_t* pPos, const char* pItem) {
866
0
  if (NULL == pBuf || bufLen <= 0 || NULL == pPos || NULL == pItem || *pPos >= bufLen - 1) {
867
0
    return;
868
0
  }
869
870
0
  int32_t remain = bufLen - *pPos;
871
0
  int32_t written = snprintf(pBuf + *pPos, remain, "%s%s", (*pPos > 0 ? "; " : ""), pItem);
872
0
  if (written < 0) {
873
0
    return;
874
0
  }
875
876
0
  if (written >= remain) {
877
0
    *pPos = bufLen - 1;
878
0
    pBuf[bufLen - 1] = '\0';
879
0
    return;
880
0
  }
881
882
0
  *pPos += written;
883
0
}
884
885
static void fqAppendSlotKeyAttempt(char* pBuf, int32_t bufLen, int32_t* pPos, const char* pLabel, const char* pKey,
886
0
                                   bool hit1, bool hit2, const char* pHash1Label, const char* pHash2Label) {
887
0
  if (NULL == pLabel || NULL == pKey || '\0' == pKey[0]) {
888
0
    return;
889
0
  }
890
891
0
  char item[256] = {0};
892
0
  if (NULL != pHash2Label) {
893
0
    (void)snprintf(item, sizeof(item), "%s=%s[%s=%d,%s=%d]", pLabel, pKey, pHash1Label, (int)hit1, pHash2Label,
894
0
                   (int)hit2);
895
0
  } else {
896
0
    (void)snprintf(item, sizeof(item), "%s=%s[%s=%d]", pLabel, pKey, pHash1Label, (int)hit1);
897
0
  }
898
0
  fqAppendSlotDiagItem(pBuf, bufLen, pPos, item);
899
0
}
900
901
0
static bool fqSlotKeyMatchesFocus(const char* pKey, const char* pFocus) {
902
0
  return (NULL != pKey && NULL != pFocus && '\0' != pFocus[0] && NULL != strstr(pKey, pFocus));
903
0
}
904
905
static void fqBuildSlotHashSummary(SHashObj* pHash, const char* pFocus1, const char* pFocus2, const char* pFocus3,
906
0
                                   char* pBuf, int32_t bufLen) {
907
0
  if (NULL == pBuf || bufLen <= 0) {
908
0
    return;
909
0
  }
910
911
0
  pBuf[0] = '\0';
912
0
  if (NULL == pHash) {
913
0
    (void)snprintf(pBuf, bufLen, "size=0 hash=(nil)");
914
0
    return;
915
0
  }
916
917
0
  char    focusBuf[512] = {0};
918
0
  char    sampleBuf[512] = {0};
919
0
  int32_t focusPos = 0;
920
0
  int32_t samplePos = 0;
921
0
  int32_t focusCnt = 0;
922
0
  int32_t sampleCnt = 0;
923
0
  int32_t iterCnt = 0;
924
0
  void*   pIter = taosHashIterate(pHash, NULL);
925
0
  while (NULL != pIter && iterCnt < 64) {
926
0
    size_t      keyLen = 0;
927
0
    const char* pKey = taosHashGetKey(pIter, &keyLen);
928
0
    if (NULL != pKey && '\0' != pKey[0]) {
929
0
      if (sampleCnt < 8) {
930
0
        fqAppendSlotDiagItem(sampleBuf, sizeof(sampleBuf), &samplePos, pKey);
931
0
        ++sampleCnt;
932
0
      }
933
0
      if (focusCnt < 6 &&
934
0
          (fqSlotKeyMatchesFocus(pKey, pFocus1) || fqSlotKeyMatchesFocus(pKey, pFocus2) ||
935
0
           fqSlotKeyMatchesFocus(pKey, pFocus3))) {
936
0
        fqAppendSlotDiagItem(focusBuf, sizeof(focusBuf), &focusPos, pKey);
937
0
        ++focusCnt;
938
0
      }
939
0
    }
940
0
    ++iterCnt;
941
0
    pIter = taosHashIterate(pHash, pIter);
942
0
  }
943
944
0
  (void)snprintf(pBuf, bufLen, "size=%d iter=%d focus=[%s] sample=[%s]", (int32_t)taosHashGetSize(pHash), iterCnt,
945
0
                 ('\0' != focusBuf[0] ? focusBuf : "-"), ('\0' != sampleBuf[0] ? sampleBuf : "-"));
946
0
}
947
948
static void fqLogSlotLookupMiss(const char* pTag, const SColumnNode* pCol, const char* pRawKey, const char* pProjKey,
949
                                SHashObj* pHash1, const char* pHash1Label, SHashObj* pHash2,
950
                                const char* pHash2Label, SHashObj* pProjHash1, const char* pProjHash1Label,
951
0
                                SHashObj* pProjHash2, const char* pProjHash2Label) {
952
0
  if (NULL == pTag || NULL == pCol) {
953
0
    return;
954
0
  }
955
956
0
  char attempts[1024] = {0};
957
0
  int32_t attemptsPos = 0;
958
959
0
  fqAppendSlotKeyAttempt(attempts, sizeof(attempts), &attemptsPos, "raw", pRawKey,
960
0
                         fqSlotLookupHasHit(pHash1, pRawKey, strlen(pRawKey)),
961
0
                         fqSlotLookupHasHit(pHash2, pRawKey, strlen(pRawKey)), pHash1Label, pHash2Label);
962
963
0
  if (NULL != pProjKey && '\0' != pProjKey[0]) {
964
0
    fqAppendSlotKeyAttempt(attempts, sizeof(attempts), &attemptsPos, "projRef", pProjKey,
965
0
                           fqSlotLookupHasHit(pProjHash1, pProjKey, strlen(pProjKey)),
966
0
                           fqSlotLookupHasHit(pProjHash2, pProjKey, strlen(pProjKey)), pProjHash1Label,
967
0
                           pProjHash2Label);
968
0
  }
969
970
0
  char*   pKey = NULL;
971
0
  int32_t keyLen = 0;
972
0
  int32_t keyCap = 0;
973
0
  int32_t code = fqBuildUnqualifiedSlotKey(pCol->colName, 64, &pKey, &keyLen, &keyCap);
974
0
  if (TSDB_CODE_SUCCESS == code) {
975
0
    fqAppendSlotKeyAttempt(attempts, sizeof(attempts), &attemptsPos, "plainCol", pKey,
976
0
                           fqSlotLookupHasHit(pHash1, pKey, keyLen), fqSlotLookupHasHit(pHash2, pKey, keyLen),
977
0
                           pHash1Label, pHash2Label);
978
0
  }
979
0
  taosMemoryFree(pKey);
980
981
0
  pKey = NULL;
982
0
  keyLen = 0;
983
0
  keyCap = 0;
984
0
  if ('\0' != pCol->node.aliasName[0]) {
985
0
    code = fqBuildUnqualifiedSlotKey(pCol->node.aliasName, 64, &pKey, &keyLen, &keyCap);
986
0
    if (TSDB_CODE_SUCCESS == code) {
987
0
      fqAppendSlotKeyAttempt(attempts, sizeof(attempts), &attemptsPos, "alias", pKey,
988
0
                             fqSlotLookupHasHit(pHash1, pKey, keyLen), fqSlotLookupHasHit(pHash2, pKey, keyLen),
989
0
                             pHash1Label, pHash2Label);
990
0
    }
991
0
    taosMemoryFree(pKey);
992
0
  }
993
994
0
  pKey = NULL;
995
0
  keyLen = 0;
996
0
  keyCap = 0;
997
0
  if ('\0' != pCol->node.userAlias[0]) {
998
0
    code = fqBuildUnqualifiedSlotKey(pCol->node.userAlias, 64, &pKey, &keyLen, &keyCap);
999
0
    if (TSDB_CODE_SUCCESS == code) {
1000
0
      fqAppendSlotKeyAttempt(attempts, sizeof(attempts), &attemptsPos, "userAlias", pKey,
1001
0
                             fqSlotLookupHasHit(pHash1, pKey, keyLen), fqSlotLookupHasHit(pHash2, pKey, keyLen),
1002
0
                             pHash1Label, pHash2Label);
1003
0
    }
1004
0
    taosMemoryFree(pKey);
1005
0
  }
1006
1007
0
  pKey = NULL;
1008
0
  keyLen = 0;
1009
0
  keyCap = 0;
1010
0
  if ('\0' != pCol->tableAlias[0]) {
1011
0
    code = fqBuildQualifiedSlotKey(pCol->tableAlias, pCol->colName, 64, &pKey, &keyLen, &keyCap);
1012
0
    if (TSDB_CODE_SUCCESS == code) {
1013
0
      fqAppendSlotKeyAttempt(attempts, sizeof(attempts), &attemptsPos, "tableAlias.col", pKey,
1014
0
                             fqSlotLookupHasHit(pHash1, pKey, keyLen), fqSlotLookupHasHit(pHash2, pKey, keyLen),
1015
0
                             pHash1Label, pHash2Label);
1016
0
    }
1017
0
    taosMemoryFree(pKey);
1018
0
  }
1019
1020
0
  pKey = NULL;
1021
0
  keyLen = 0;
1022
0
  keyCap = 0;
1023
0
  if (pCol->hasRef) {
1024
0
    code = fqBuildRefSlotKey(pCol, 64, &pKey, &keyLen, &keyCap);
1025
0
    if (TSDB_CODE_SUCCESS == code) {
1026
0
      fqAppendSlotKeyAttempt(attempts, sizeof(attempts), &attemptsPos, "refKey", pKey,
1027
0
                             fqSlotLookupHasHit(pHash1, pKey, keyLen), fqSlotLookupHasHit(pHash2, pKey, keyLen),
1028
0
                             pHash1Label, pHash2Label);
1029
0
    }
1030
0
    taosMemoryFree(pKey);
1031
0
  }
1032
1033
0
  char hash1Summary[640] = {0};
1034
0
  char hash2Summary[640] = {0};
1035
0
  char projHash1Summary[640] = {0};
1036
0
  char projHash2Summary[640] = {0};
1037
0
  fqBuildSlotHashSummary(pHash1, pCol->colName, pCol->node.aliasName, pCol->node.userAlias, hash1Summary,
1038
0
                         sizeof(hash1Summary));
1039
0
  fqBuildSlotHashSummary(pHash2, pCol->colName, pCol->node.aliasName, pCol->node.userAlias, hash2Summary,
1040
0
                         sizeof(hash2Summary));
1041
0
  fqBuildSlotHashSummary(pProjHash1, pCol->colName, pCol->node.aliasName, pCol->node.userAlias, projHash1Summary,
1042
0
                         sizeof(projHash1Summary));
1043
0
  fqBuildSlotHashSummary(pProjHash2, pCol->colName, pCol->node.aliasName, pCol->node.userAlias, projHash2Summary,
1044
0
                         sizeof(projHash2Summary));
1045
1046
0
  planDebug("%s: attempts=%s", pTag, attempts);
1047
0
  planDebug("%s: normalHash %s={%s} %s={%s}", pTag, pHash1Label, hash1Summary,
1048
0
            (NULL != pHash2Label ? pHash2Label : "hash2"), hash2Summary);
1049
0
  planDebug("%s: projHash %s={%s} %s={%s}", pTag, pProjHash1Label, projHash1Summary,
1050
0
            (NULL != pProjHash2Label ? pProjHash2Label : "projHash2"), projHash2Summary);
1051
0
}
1052
1053
0
static EDealRes doSetSlotId(SNode* pNode, void* pContext) {
1054
0
  if (QUERY_NODE_COLUMN == nodeType(pNode) && 0 != strcmp(((SColumnNode*)pNode)->colName, "*")) {
1055
0
    SSetSlotIdCxt* pCxt = (SSetSlotIdCxt*)pContext;
1056
0
    SColumnNode*   pCol = (SColumnNode*)pNode;
1057
0
    bool preferLeft = NULL != pCxt->pLeftTableAlias && '\0' != pCxt->pLeftTableAlias[0] &&
1058
0
                      0 == strcmp(pCol->tableAlias, pCxt->pLeftTableAlias);
1059
0
    bool preferRight = !preferLeft && NULL != pCxt->pRightTableAlias && '\0' != pCxt->pRightTableAlias[0] &&
1060
0
                       0 == strcmp(pCol->tableAlias, pCxt->pRightTableAlias);
1061
0
    SHashObj* pFirstHash = preferRight ? pCxt->pRightHash : pCxt->pLeftHash;
1062
0
    SHashObj* pSecondHash = preferRight ? pCxt->pLeftHash : pCxt->pRightHash;
1063
0
    SHashObj* pFirstProjIdxHash = preferRight ? pCxt->pRightProdIdxHash : pCxt->pLeftProjIdxHash;
1064
0
    SHashObj* pSecondProjIdxHash = preferRight ? pCxt->pLeftProjIdxHash : pCxt->pRightProdIdxHash;
1065
0
    char*          name = NULL;
1066
0
    char           rawLookupKey[TSDB_TABLE_FNAME_LEN + TSDB_COL_NAME_LEN + 96] = {0};
1067
0
    char           projLookupKey[TSDB_TABLE_FNAME_LEN + TSDB_COL_NAME_LEN + 96] = {0};
1068
0
    int32_t        len = 0;
1069
0
    int32_t        cap = 0;
1070
0
    pCxt->errCode = getSlotKey(pNode, NULL, &name, &len, 64, &cap);
1071
0
    if (TSDB_CODE_SUCCESS != pCxt->errCode) {
1072
0
      return DEAL_RES_ERROR;
1073
0
    }
1074
0
    if (NULL != name) {
1075
0
      tstrncpy(rawLookupKey, name, sizeof(rawLookupKey));
1076
0
    }
1077
0
    SSlotIndex* pIndex = NULL;
1078
0
    if (((SColumnNode*)pNode)->projRefIdx > 0) {
1079
0
      snprintf(name + strlen(name), cap, "_%d", ((SColumnNode*)pNode)->projRefIdx);
1080
0
      tstrncpy(projLookupKey, name, sizeof(projLookupKey));
1081
0
      pIndex = taosHashGet(pFirstProjIdxHash, name, strlen(name));
1082
0
      if (!pIndex) {
1083
0
        pIndex = taosHashGet(pSecondProjIdxHash, name, strlen(name));
1084
0
      }
1085
0
    }
1086
0
    if (NULL == pIndex) {
1087
0
      SSlotIndex* pFullIdx = taosHashGet(pFirstHash, name, len);
1088
0
      if (NULL == pFullIdx) {
1089
0
        pFullIdx = taosHashGet(pSecondHash, name, len);
1090
0
      }
1091
0
      if (NULL != pFullIdx) {
1092
0
        pIndex = pFullIdx;
1093
0
      }
1094
0
      if (NULL == pIndex && false == pCol->hasRef && false == pCol->hasDep) {
1095
0
        int32_t colLen = (int32_t)strlen(pCol->colName);
1096
0
        if (colLen > 0) {
1097
0
          pIndex = taosHashGet(pFirstHash, pCol->colName, colLen);
1098
0
          if (NULL == pIndex) {
1099
0
            pIndex = taosHashGet(pSecondHash, pCol->colName, colLen);
1100
0
          }
1101
0
        }
1102
1103
0
        if (NULL == pIndex && ((SExprNode*)pCol)->aliasName[0] != '\0') {
1104
0
          int32_t aliasLen = (int32_t)strlen(((SExprNode*)pCol)->aliasName);
1105
0
          pIndex = taosHashGet(pFirstHash, ((SExprNode*)pCol)->aliasName, aliasLen);
1106
0
          if (NULL == pIndex) {
1107
0
            pIndex = taosHashGet(pSecondHash, ((SExprNode*)pCol)->aliasName, aliasLen);
1108
0
          }
1109
0
        }
1110
0
      }
1111
0
    }
1112
0
    if (NULL == pIndex) {
1113
0
      name[len] = 0;
1114
0
      pIndex = taosHashGet(pFirstHash, name, len);
1115
0
      if (NULL == pIndex) {
1116
0
        pIndex = taosHashGet(pSecondHash, name, len);
1117
0
      }
1118
0
    }
1119
    // pIndex is definitely not NULL, otherwise it is a bug
1120
0
    if (NULL == pIndex) {
1121
0
      planError("doSetSlotId failed, invalid slot name %s (colId=%d, tableAlias=%s, tableName=%s)",
1122
0
                name,
1123
0
                (QUERY_NODE_COLUMN == nodeType(pNode)) ? ((SColumnNode*)pNode)->colId : -1,
1124
0
                (QUERY_NODE_COLUMN == nodeType(pNode)) ? ((SColumnNode*)pNode)->tableAlias : "?",
1125
0
                (QUERY_NODE_COLUMN == nodeType(pNode)) ? ((SColumnNode*)pNode)->tableName : "?");
1126
0
      if (QUERY_NODE_COLUMN == nodeType(pNode)) {
1127
0
        SColumnNode* pDbgCol = (SColumnNode*)pNode;
1128
0
        planError("FQ-slotMiss: colName=%s aliasName=%s userAlias=%s hasRef=%d hasDep=%d projRefIdx=%d projIdx=%d relatedTo=%d leftHash=%p rightHash=%p",
1129
0
                  pDbgCol->colName, pDbgCol->node.aliasName, pDbgCol->node.userAlias, (int)pDbgCol->hasRef,
1130
0
                  (int)pDbgCol->hasDep, (int)pDbgCol->projRefIdx, (int)pDbgCol->projIdx,
1131
0
                  (int)pDbgCol->node.relatedTo, pCxt->pLeftHash, pCxt->pRightHash);
1132
0
        fqLogSlotLookupMiss("FQ-slotMissDiag", pDbgCol, rawLookupKey, projLookupKey, pCxt->pLeftHash, "leftHash",
1133
0
                            pCxt->pRightHash, "rightHash", pCxt->pLeftProjIdxHash, "leftProjIdxHash",
1134
0
                            pCxt->pRightProdIdxHash, "rightProjIdxHash");
1135
0
      }
1136
0
      pCxt->errCode = TSDB_CODE_PLAN_SLOT_NOT_FOUND;
1137
0
      taosMemoryFree(name);
1138
0
      return DEAL_RES_ERROR;
1139
0
    }
1140
0
    ((SColumnNode*)pNode)->dataBlockId = pIndex->dataBlockId;
1141
0
    int16_t assignedSlot = ((SSlotIdInfo*)taosArrayGet(pIndex->pSlotIdsInfo, 0))->slotId;
1142
0
    qDebug("doSetSlotId: colName='%s' aliasName='%s' tableAlias='%s' numSlots=%d assignedSlotId=%d",
1143
0
           ((SColumnNode*)pNode)->colName,
1144
0
           ((SExprNode*)pNode)->aliasName,
1145
0
           ((SColumnNode*)pNode)->tableAlias,
1146
0
           (int32_t)taosArrayGetSize(pIndex->pSlotIdsInfo),
1147
0
           (int32_t)assignedSlot);
1148
0
    ((SColumnNode*)pNode)->slotId = assignedSlot;
1149
0
    taosMemoryFree(name);
1150
0
    return DEAL_RES_IGNORE_CHILD;
1151
0
  }
1152
0
  return DEAL_RES_CONTINUE;
1153
0
}
1154
1155
0
static EDealRes doSetMultiTableSlotId(SNode* pNode, void* pContext) {
1156
0
  if (QUERY_NODE_COLUMN == nodeType(pNode) && 0 != strcmp(((SColumnNode*)pNode)->colName, "*")) {
1157
0
    SMultiTableSetSlotIdCxt* pCxt = (SMultiTableSetSlotIdCxt*)pContext;
1158
0
    char*                    name = NULL;
1159
0
    char                     rawLookupKey[TSDB_TABLE_FNAME_LEN + TSDB_COL_NAME_LEN + 96] = {0};
1160
0
    char                     projLookupKey[TSDB_TABLE_FNAME_LEN + TSDB_COL_NAME_LEN + 96] = {0};
1161
0
    int32_t                  len = 0;
1162
0
    int32_t cap = 0;
1163
0
    SColumnNode*             pCol = (SColumnNode*)pNode;
1164
0
    if (pCxt->isVtb && ((!pCol->hasRef && pCol->colType != COLUMN_TYPE_TAG && '\0' != pCol->tableAlias[0]) || (pCol->colId == PRIMARYKEY_TIMESTAMP_COL_ID))) {
1165
      // set slot id for :
1166
      // 1. column with ref
1167
      // 2. tag column
1168
      // 3. pseudo column function
1169
1170
      // even if ts column has ref, still skip it (VTB only).
1171
0
      return DEAL_RES_CONTINUE;
1172
0
    }
1173
  
1174
0
    pCxt->errCode = getSlotKey(pNode, NULL, &name, &len, 16, &cap);
1175
0
    if (TSDB_CODE_SUCCESS != pCxt->errCode) {
1176
0
      return DEAL_RES_ERROR;
1177
0
    }
1178
0
    if (NULL != name) {
1179
0
      tstrncpy(rawLookupKey, name, sizeof(rawLookupKey));
1180
0
    }
1181
0
    SSlotIndex* pIndex = NULL;
1182
0
    if (pCol->projRefIdx > 0) {
1183
0
      snprintf(name + strlen(name), cap - strlen(name), "_%d", pCol->projRefIdx);
1184
0
      tstrncpy(projLookupKey, name, sizeof(projLookupKey));
1185
0
      SSlotIndex** ppIndex = (SSlotIndex**)taosHashGet(pCxt->mergedProjIdxHash, name, strlen(name));
1186
0
      if (ppIndex) {
1187
0
        pIndex = *ppIndex;
1188
0
      }
1189
0
    } else {
1190
0
      SSlotIndex** ppIndex = (SSlotIndex**)taosHashGet(pCxt->mergedHash, name, len);
1191
0
      if (ppIndex) {
1192
0
        pIndex = *ppIndex;
1193
0
      }
1194
0
    }
1195
    // pIndex is definitely not NULL, otherwise it is a bug
1196
0
    if (NULL == pIndex) {
1197
0
      planError("doSetMultiTableSlotId failed, invalid slot name %s (col=%s, db=%s, tbl=%s, alias=%s, hasRef=%d, refDb=%s, refTbl=%s, refCol=%s, colType=%d, len=%d)",
1198
0
                name, pCol->colName, pCol->dbName, pCol->tableName, pCol->tableAlias,
1199
0
                pCol->hasRef, pCol->refDbName, pCol->refTableName, pCol->refColName,
1200
0
                pCol->colType, len);
1201
0
      fqLogSlotLookupMiss("FQ-multiSlotMissDiag", pCol, rawLookupKey, projLookupKey, pCxt->mergedHash, "mergedHash",
1202
0
                          NULL, NULL, pCxt->mergedProjIdxHash, "mergedProjIdxHash", NULL, NULL);
1203
0
      pCxt->errCode = TSDB_CODE_PLAN_SLOT_NOT_FOUND;
1204
0
      taosMemoryFree(name);
1205
0
      return DEAL_RES_ERROR;
1206
0
    }
1207
0
    pCol->dataBlockId = pIndex->dataBlockId;
1208
0
    pCol->slotId = ((SSlotIdInfo*)taosArrayGet(pIndex->pSlotIdsInfo, 0))->slotId;
1209
0
    taosMemoryFree(name);
1210
0
    return DEAL_RES_IGNORE_CHILD;
1211
0
  }
1212
0
  return DEAL_RES_CONTINUE;
1213
0
}
1214
1215
static int32_t setNodeSlotIdImpl(SPhysiPlanContext* pCxt, int64_t leftDataBlockId, int64_t rightDataBlockId,
1216
                                 const char* pLeftTableAlias, const char* pRightTableAlias, SNode* pNode,
1217
0
                                 SNode** pOutput) {
1218
0
  if (NULL == pNode) {
1219
0
    return TSDB_CODE_SUCCESS;
1220
0
  }
1221
1222
0
  SNode*  pRes = NULL;
1223
0
  int32_t code = nodesCloneNode(pNode, &pRes);
1224
0
  if (NULL == pRes) {
1225
0
    return code;
1226
0
  }
1227
1228
0
  SSetSlotIdCxt cxt = {
1229
0
      .errCode = TSDB_CODE_SUCCESS,
1230
0
      .pLeftHash = taosArrayGetP(pCxt->pLocationHelper, leftDataBlockId),
1231
0
      .pLeftProjIdxHash = taosArrayGetP(pCxt->pProjIdxLocHelper, leftDataBlockId),
1232
0
      .pRightHash = (rightDataBlockId < 0 ? NULL : taosArrayGetP(pCxt->pLocationHelper, rightDataBlockId)),
1233
0
      .pRightProdIdxHash = (rightDataBlockId < 0 ? NULL : taosArrayGetP(pCxt->pProjIdxLocHelper, rightDataBlockId)),
1234
0
      .pLeftTableAlias = pLeftTableAlias,
1235
0
      .pRightTableAlias = pRightTableAlias};
1236
0
  nodesWalkExpr(pRes, doSetSlotId, &cxt);
1237
0
  if (TSDB_CODE_SUCCESS != cxt.errCode) {
1238
0
    nodesDestroyNode(pRes);
1239
0
    return cxt.errCode;
1240
0
  }
1241
0
  *pOutput = pRes;
1242
0
  return TSDB_CODE_SUCCESS;
1243
0
}
1244
1245
static int32_t setNodeSlotIdWithAliases(SPhysiPlanContext* pCxt, int64_t leftDataBlockId, int64_t rightDataBlockId,
1246
                                        const char* pLeftAlias, const char* pRightAlias, SNode* pNode,
1247
0
                                        SNode** pOutput) {
1248
0
  return setNodeSlotIdImpl(pCxt, leftDataBlockId, rightDataBlockId, pLeftAlias, pRightAlias, pNode, pOutput);
1249
0
}
1250
1251
static int32_t setNodeSlotId(SPhysiPlanContext* pCxt, int64_t leftDataBlockId, int64_t rightDataBlockId, SNode* pNode,
1252
0
                             SNode** pOutput) {
1253
0
  return setNodeSlotIdWithAliases(pCxt, leftDataBlockId, rightDataBlockId, NULL, NULL, pNode, pOutput);
1254
0
}
1255
1256
static int32_t setListSlotId(SPhysiPlanContext* pCxt, int64_t leftDataBlockId, int64_t rightDataBlockId,
1257
0
                             const SNodeList* pList, SNodeList** pOutput) {
1258
0
  if (NULL == pList) {
1259
0
    return TSDB_CODE_SUCCESS;
1260
0
  }
1261
1262
0
  SNodeList* pRes = NULL;
1263
0
  int32_t    code = nodesCloneList(pList, &pRes);
1264
0
  if (NULL == pRes) {
1265
0
    return code;
1266
0
  }
1267
1268
0
  SSetSlotIdCxt cxt = {
1269
0
      .errCode = TSDB_CODE_SUCCESS,
1270
0
      .pLeftHash = taosArrayGetP(pCxt->pLocationHelper, leftDataBlockId),
1271
0
      .pLeftProjIdxHash = taosArrayGetP(pCxt->pProjIdxLocHelper, leftDataBlockId),
1272
0
      .pRightHash = (rightDataBlockId < 0 ? NULL : taosArrayGetP(pCxt->pLocationHelper, rightDataBlockId)),
1273
0
      .pRightProdIdxHash = (rightDataBlockId < 0 ? NULL : taosArrayGetP(pCxt->pProjIdxLocHelper, rightDataBlockId))};
1274
0
  nodesWalkExprs(pRes, doSetSlotId, &cxt);
1275
0
  if (TSDB_CODE_SUCCESS != cxt.errCode) {
1276
0
    nodesDestroyList(pRes);
1277
0
    return cxt.errCode;
1278
0
  }
1279
0
  *pOutput = pRes;
1280
0
  return TSDB_CODE_SUCCESS;
1281
0
}
1282
1283
0
static int32_t mergeSlotHashForChildren(SNodeList* pChild, SArray* hashArray, SHashObj** ppMerged) {
1284
0
  int32_t capacity = 0;
1285
0
  SNode*  node = NULL;
1286
0
  FOREACH(node, pChild) {
1287
0
    SPhysiNode* pChildNode = (SPhysiNode*)node;
1288
0
    if (pChildNode->pOutputDataBlockDesc && pChildNode->pOutputDataBlockDesc->pSlots) {
1289
0
      capacity += LIST_LENGTH(pChildNode->pOutputDataBlockDesc->pSlots);
1290
0
    }
1291
0
  }
1292
0
  if (capacity <= 0) {
1293
0
    capacity = 1;
1294
0
  }
1295
1296
0
  SHashObj* pMerged =
1297
0
      taosHashInit(capacity, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), false, HASH_NO_LOCK);
1298
0
  if (NULL == pMerged) {
1299
0
    return terrno;
1300
0
  }
1301
1302
0
  FOREACH(node, pChild) {
1303
0
    SPhysiNode* pChildNode = (SPhysiNode*)node;
1304
0
    if (NULL == pChildNode->pOutputDataBlockDesc) {
1305
0
      continue;
1306
0
    }
1307
0
    SHashObj* pSrc = taosArrayGetP(hashArray, (size_t)pChildNode->pOutputDataBlockDesc->dataBlockId);
1308
0
    if (NULL == pSrc) {
1309
0
      continue;
1310
0
    }
1311
0
    void* pIt = taosHashIterate(pSrc, NULL);
1312
0
    while (pIt) {
1313
0
      size_t keyLen = 0;
1314
0
      char*  pKey = taosHashGetKey(pIt, &keyLen);
1315
0
      if (NULL == taosHashGet(pMerged, pKey, keyLen)) {
1316
0
        SSlotIndex* pIndex = (SSlotIndex*)pIt;
1317
0
        int32_t     code = taosHashPut(pMerged, pKey, keyLen, &pIndex, POINTER_BYTES);
1318
0
        if (TSDB_CODE_SUCCESS != code) {
1319
0
          taosHashCleanup(pMerged);
1320
0
          return code;
1321
0
        }
1322
0
      }
1323
0
      pIt = taosHashIterate(pSrc, pIt);
1324
0
    }
1325
0
  }
1326
1327
0
  *ppMerged = pMerged;
1328
0
  return TSDB_CODE_SUCCESS;
1329
0
}
1330
1331
static int32_t setMultiBlockSlotId(SPhysiPlanContext* pCxt, SNodeList* pChild, bool isVtb, const SNodeList* pList,
1332
0
                                   SNodeList** pOutput) {
1333
0
  int32_t code = TSDB_CODE_SUCCESS;
1334
0
  if (NULL == pList) {
1335
0
    PLAN_RET(code);
1336
0
  }
1337
1338
0
  SNodeList* pRes = NULL;
1339
0
  SHashObj*  mergedHash = NULL;
1340
0
  SHashObj*  mergedProjIdxHash = NULL;
1341
1342
0
  PLAN_ERR_JRET(nodesCloneList(pList, &pRes));
1343
1344
0
  PLAN_ERR_JRET(mergeSlotHashForChildren(pChild, pCxt->pLocationHelper, &mergedHash));
1345
0
  PLAN_ERR_JRET(mergeSlotHashForChildren(pChild, pCxt->pProjIdxLocHelper, &mergedProjIdxHash));
1346
1347
0
  SMultiTableSetSlotIdCxt cxt = {
1348
0
    .errCode = TSDB_CODE_SUCCESS,
1349
0
    .hashArray = pCxt->pLocationHelper,
1350
0
    .projIdxHashArray = pCxt->pProjIdxLocHelper,
1351
0
    .mergedHash = mergedHash,
1352
0
    .mergedProjIdxHash = mergedProjIdxHash,
1353
0
    .pChild = pChild,
1354
0
    .isVtb = isVtb
1355
0
  };
1356
1357
0
  nodesWalkExprs(pRes, doSetMultiTableSlotId, &cxt);
1358
0
  PLAN_ERR_JRET(cxt.errCode);
1359
1360
0
  *pOutput = pRes;
1361
0
  taosHashCleanup(mergedHash);
1362
0
  taosHashCleanup(mergedProjIdxHash);
1363
0
  return code;
1364
0
_return:
1365
0
  if (mergedHash) {
1366
0
    taosHashCleanup(mergedHash);
1367
0
  }
1368
0
  if (mergedProjIdxHash) {
1369
0
    taosHashCleanup(mergedProjIdxHash);
1370
0
  }
1371
0
  nodesDestroyList(pRes);
1372
0
  return code;
1373
0
}
1374
1375
0
static SPhysiNode* makePhysiNode(SPhysiPlanContext* pCxt, SLogicNode* pLogicNode, ENodeType type) {
1376
0
  SPhysiNode* pPhysiNode = NULL;
1377
0
  int32_t     code = nodesMakeNode(type, (SNode**)&pPhysiNode);
1378
0
  if (NULL == pPhysiNode) {
1379
0
    terrno = code;
1380
0
    return NULL;
1381
0
  }
1382
1383
0
  TSWAP(pPhysiNode->pLimit, pLogicNode->pLimit);
1384
0
  TSWAP(pPhysiNode->pSlimit, pLogicNode->pSlimit);
1385
0
  pPhysiNode->dynamicOp = pLogicNode->dynamicOp;
1386
0
  pPhysiNode->inputTsOrder = pLogicNode->inputTsOrder;
1387
0
  pPhysiNode->outputTsOrder = pLogicNode->outputTsOrder;
1388
0
  pPhysiNode->requireDataOrder = pLogicNode->requireDataOrder;
1389
0
  pPhysiNode->resultDataOrder = pLogicNode->resultDataOrder;
1390
1391
0
  code = createDataBlockDesc(pCxt, pLogicNode->pTargets, &pPhysiNode->pOutputDataBlockDesc);
1392
0
  if (TSDB_CODE_SUCCESS != code) {
1393
0
    planError("FQ-Slots: makePhysiNode failed type=%d logicType=%d targets=%d code=0x%x",
1394
0
              (int)type, (int)nodeType((SNode*)pLogicNode),
1395
0
              pLogicNode->pTargets ? LIST_LENGTH(pLogicNode->pTargets) : 0, code);
1396
0
    nodesDestroyNode((SNode*)pPhysiNode);
1397
0
    terrno = code;
1398
0
    return NULL;
1399
0
  }
1400
0
  pPhysiNode->pOutputDataBlockDesc->precision = pLogicNode->precision;
1401
0
  return pPhysiNode;
1402
0
}
1403
1404
0
static int32_t setConditionsSlotId(SPhysiPlanContext* pCxt, const SLogicNode* pLogicNode, SPhysiNode* pPhysiNode) {
1405
0
  if (NULL != pLogicNode->pConditions) {
1406
0
    planDebug("FQ-slotCond: logicType=%d physiType=%d dataBlockId=%" PRId64 " conditions=%p",
1407
0
              (int)nodeType((SNode*)pLogicNode), (int)nodeType((SNode*)pPhysiNode),
1408
0
              pPhysiNode->pOutputDataBlockDesc ? pPhysiNode->pOutputDataBlockDesc->dataBlockId : -1,
1409
0
              pLogicNode->pConditions);
1410
0
    return setNodeSlotId(pCxt, pPhysiNode->pOutputDataBlockDesc->dataBlockId, -1, pLogicNode->pConditions,
1411
0
                         &pPhysiNode->pConditions);
1412
0
  }
1413
0
  return TSDB_CODE_SUCCESS;
1414
0
}
1415
1416
0
static bool isTableScanPhysiNode(const SScanPhysiNode* pScanPhysiNode) {
1417
0
  switch (nodeType(pScanPhysiNode)) {
1418
0
    case QUERY_NODE_PHYSICAL_PLAN_TABLE_SCAN:
1419
0
    case QUERY_NODE_PHYSICAL_PLAN_TABLE_MERGE_SCAN:
1420
0
    case QUERY_NODE_PHYSICAL_PLAN_STREAM_SCAN:
1421
0
      return true;
1422
0
    default:
1423
0
      return false;
1424
0
  }
1425
0
}
1426
1427
static int32_t setTableScanPrimaryCondSlotId(SPhysiPlanContext* pCxt, const SScanLogicNode* pScanLogicNode,
1428
0
                                             SScanPhysiNode* pScanPhysiNode) {
1429
0
  if (NULL == pScanLogicNode->pPrimaryCond || !isTableScanPhysiNode(pScanPhysiNode)) {
1430
0
    return TSDB_CODE_SUCCESS;
1431
0
  }
1432
1433
0
  STableScanPhysiNode* pTableScan = (STableScanPhysiNode*)pScanPhysiNode;
1434
0
  return setNodeSlotId(pCxt, pScanPhysiNode->node.pOutputDataBlockDesc->dataBlockId, -1, pScanLogicNode->pPrimaryCond,
1435
0
                       &pTableScan->pPrimaryCond);
1436
0
}
1437
1438
0
static int32_t colIdCompare(const void* pLeft, const void* pRight) {
1439
0
  SColumnNode* pLeftCol = *(SColumnNode**)pLeft;
1440
0
  SColumnNode* pRightCol = *(SColumnNode**)pRight;
1441
0
  return pLeftCol->colId > pRightCol->colId ? 1 : -1;
1442
0
}
1443
1444
0
static int32_t sortScanCols(SNodeList* pScanCols) {
1445
0
  SArray* pArray = taosArrayInit(LIST_LENGTH(pScanCols), POINTER_BYTES);
1446
0
  if (NULL == pArray) {
1447
0
    return terrno;
1448
0
  }
1449
1450
0
  int32_t code = 0;
1451
0
  SNode*  pCol = NULL;
1452
0
  FOREACH(pCol, pScanCols) {
1453
0
    if (NULL == taosArrayPush(pArray, &pCol)) {
1454
0
      code = terrno;
1455
0
      break;
1456
0
    }
1457
0
  }
1458
0
  if (TSDB_CODE_SUCCESS != code) {
1459
0
    taosArrayDestroy(pArray);
1460
0
    return code;
1461
0
  }
1462
0
  taosArraySort(pArray, colIdCompare);
1463
1464
0
  int32_t index = 0;
1465
0
  FOREACH(pCol, pScanCols) { REPLACE_NODE(taosArrayGetP(pArray, index++)); }
1466
0
  taosArrayDestroy(pArray);
1467
1468
0
  return TSDB_CODE_SUCCESS;
1469
0
}
1470
1471
0
static bool physiScanHasColumn(SNodeList* pList, const SColumnNode* pCol) {
1472
0
  if (pList == NULL || pCol == NULL) {
1473
0
    return false;
1474
0
  }
1475
1476
0
  SNode* pNode = NULL;
1477
0
  FOREACH(pNode, pList) {
1478
0
    SColumnNode* pExist = NULL;
1479
0
    if (nodeType(pNode) == QUERY_NODE_TARGET) {
1480
0
      STargetNode* pTarget = (STargetNode*)pNode;
1481
0
      if (pTarget->pExpr != NULL && nodeType(pTarget->pExpr) == QUERY_NODE_COLUMN) {
1482
0
        pExist = (SColumnNode*)pTarget->pExpr;
1483
0
      }
1484
0
    } else if (nodeType(pNode) == QUERY_NODE_COLUMN) {
1485
0
      pExist = (SColumnNode*)pNode;
1486
0
    }
1487
1488
0
    if (pExist == NULL) {
1489
0
      continue;
1490
0
    }
1491
1492
0
    if (pExist->colId == pCol->colId && pExist->colType == pCol->colType && pExist->hasRef == pCol->hasRef &&
1493
0
        0 == strcmp(pExist->tableAlias, pCol->tableAlias) && 0 == strcmp(pExist->refDbName, pCol->refDbName) &&
1494
0
        0 == strcmp(pExist->refTableName, pCol->refTableName) && 0 == strcmp(pExist->refColName, pCol->refColName)) {
1495
0
      return true;
1496
0
    }
1497
0
  }
1498
1499
0
  return false;
1500
0
}
1501
1502
0
static int32_t appendPhysiScanTbnameFunc(SNodeList** ppList, const SColumnNode* pCol) {
1503
0
  if (ppList == NULL || pCol == NULL) {
1504
0
    return TSDB_CODE_SUCCESS;
1505
0
  }
1506
1507
0
  SNode* pNode = NULL;
1508
0
  FOREACH(pNode, *ppList) {
1509
0
    if (QUERY_NODE_FUNCTION == nodeType(pNode) && FUNCTION_TYPE_TBNAME == ((SFunctionNode*)pNode)->funcType) {
1510
0
      return TSDB_CODE_SUCCESS;
1511
0
    }
1512
0
  }
1513
1514
0
  SFunctionNode* pFunc = NULL;
1515
0
  int32_t        code = nodesMakeNode(QUERY_NODE_FUNCTION, (SNode**)&pFunc);
1516
0
  if (TSDB_CODE_SUCCESS != code) {
1517
0
    return code;
1518
0
  }
1519
1520
0
  tstrncpy(pFunc->functionName, "tbname", TSDB_FUNC_NAME_LEN);
1521
0
  pFunc->funcType = FUNCTION_TYPE_TBNAME;
1522
0
  pFunc->node.resType = pCol->node.resType;
1523
0
  code = fmGetFuncInfo(pFunc, NULL, 0);
1524
0
  if (TSDB_CODE_SUCCESS != code) {
1525
0
    nodesDestroyNode((SNode*)pFunc);
1526
0
    return code;
1527
0
  }
1528
  // Strict append consumes the node on both success and failure.
1529
0
  return nodesListMakeStrictAppend(ppList, (SNode*)pFunc);
1530
0
}
1531
1532
0
static int32_t appendPhysiScanColumn(SNodeList** ppList, const SColumnNode* pCol) {
1533
0
  if (ppList == NULL || pCol == NULL || physiScanHasColumn(*ppList, pCol)) {
1534
0
    return TSDB_CODE_SUCCESS;
1535
0
  }
1536
1537
0
  SNode* pClone = NULL;
1538
0
  int32_t code = nodesCloneNode((SNode*)pCol, &pClone);
1539
0
  if (TSDB_CODE_SUCCESS == code) {
1540
0
    code = nodesListMakeStrictAppend(ppList, pClone);
1541
0
  }
1542
0
  return code;
1543
0
}
1544
1545
0
static int32_t appendPhysiLocalTagsFromList(SNodeList* pSrc, SNodeList** ppDst) {
1546
0
  if (pSrc == NULL || ppDst == NULL) {
1547
0
    return TSDB_CODE_SUCCESS;
1548
0
  }
1549
1550
0
  int32_t code = TSDB_CODE_SUCCESS;
1551
0
  SNode*  pNode = NULL;
1552
0
  FOREACH(pNode, pSrc) {
1553
0
    SColumnNode* pCol = NULL;
1554
0
    if (nodeType(pNode) == QUERY_NODE_TARGET) {
1555
0
      STargetNode* pTarget = (STargetNode*)pNode;
1556
0
      if (pTarget->pExpr != NULL && nodeType(pTarget->pExpr) == QUERY_NODE_COLUMN) {
1557
0
        pCol = (SColumnNode*)pTarget->pExpr;
1558
0
      }
1559
0
    } else if (nodeType(pNode) == QUERY_NODE_COLUMN) {
1560
0
      pCol = (SColumnNode*)pNode;
1561
0
    }
1562
1563
0
    if (pCol == NULL || pCol->colType != COLUMN_TYPE_TAG || pCol->hasRef) {
1564
0
      continue;
1565
0
    }
1566
1567
0
    code = appendPhysiScanColumn(ppDst, pCol);
1568
0
    if (TSDB_CODE_SUCCESS != code) {
1569
0
      break;
1570
0
    }
1571
0
  }
1572
1573
0
  return code;
1574
0
}
1575
1576
0
static int32_t createScanCols(SPhysiPlanContext* pCxt, SScanPhysiNode* pScanPhysiNode, SNodeList* pScanCols) {
1577
0
  if (NULL == pScanCols) {
1578
0
    return TSDB_CODE_SUCCESS;
1579
0
  }
1580
1581
0
  pScanPhysiNode->pScanCols = NULL;
1582
0
  int32_t code = nodesCloneList(pScanCols, &pScanPhysiNode->pScanCols);
1583
0
  if (NULL == pScanPhysiNode->pScanCols) {
1584
0
    return code;
1585
0
  }
1586
0
  return sortScanCols(pScanPhysiNode->pScanCols);
1587
0
}
1588
1589
0
static int32_t appendConditionColsToPhysiScan(SScanPhysiNode* pScanPhysiNode, SNode* pConditions) {
1590
0
  if (NULL == pScanPhysiNode || NULL == pConditions) {
1591
0
    return TSDB_CODE_SUCCESS;
1592
0
  }
1593
1594
0
  SNodeList* pCondCols = NULL;
1595
0
  int32_t    code = nodesCollectColumnsFromNode(pConditions, NULL, COLLECT_COL_TYPE_ALL, &pCondCols);
1596
0
  if (TSDB_CODE_SUCCESS != code) {
1597
0
    return code;
1598
0
  }
1599
1600
0
  SNode* pNode = NULL;
1601
0
  FOREACH(pNode, pCondCols) {
1602
0
    if (QUERY_NODE_COLUMN != nodeType(pNode)) {
1603
0
      continue;
1604
0
    }
1605
1606
0
    SColumnNode* pCol = (SColumnNode*)pNode;
1607
0
    if (COLUMN_TYPE_TAG == pCol->colType) {
1608
0
      code = appendPhysiScanColumn(&pScanPhysiNode->pScanPseudoCols, pCol);
1609
0
    } else if (COLUMN_TYPE_TBNAME == pCol->colType) {
1610
0
      code = appendPhysiScanTbnameFunc(&pScanPhysiNode->pScanPseudoCols, pCol);
1611
0
    } else {
1612
0
      code = appendPhysiScanColumn(&pScanPhysiNode->pScanCols, pCol);
1613
0
    }
1614
0
    if (TSDB_CODE_SUCCESS != code) {
1615
0
      break;
1616
0
    }
1617
0
  }
1618
1619
0
  nodesDestroyList(pCondCols);
1620
1621
0
  if (TSDB_CODE_SUCCESS == code) {
1622
0
    code = sortScanCols(pScanPhysiNode->pScanCols);
1623
0
  }
1624
1625
0
  return code;
1626
0
}
1627
1628
static int32_t createScanPhysiNodeFinalize(SPhysiPlanContext* pCxt, SSubplan* pSubplan, SScanLogicNode* pScanLogicNode,
1629
0
                                           SScanPhysiNode* pScanPhysiNode, SPhysiNode** pPhyNode) {
1630
0
  int32_t code = createScanCols(pCxt, pScanPhysiNode, pScanLogicNode->pScanCols);
1631
0
  if (TSDB_CODE_SUCCESS == code && NULL != pScanLogicNode->pScanPseudoCols) {
1632
0
    pScanPhysiNode->pScanPseudoCols = NULL;
1633
0
    code = nodesCloneList(pScanLogicNode->pScanPseudoCols, &pScanPhysiNode->pScanPseudoCols);
1634
0
  }
1635
1636
0
  if (TSDB_CODE_SUCCESS == code) {
1637
0
    code = appendConditionColsToPhysiScan(pScanPhysiNode, pScanLogicNode->node.pConditions);
1638
0
  }
1639
1640
0
  if (TSDB_CODE_SUCCESS == code) {
1641
0
    code = addDataBlockSlots(pCxt, pScanPhysiNode->pScanCols, pScanPhysiNode->node.pOutputDataBlockDesc);
1642
0
  }
1643
1644
0
  if (TSDB_CODE_SUCCESS == code) {
1645
0
    code = addDataBlockSlots(pCxt, pScanPhysiNode->pScanPseudoCols, pScanPhysiNode->node.pOutputDataBlockDesc);
1646
0
  }
1647
1648
0
  if (TSDB_CODE_SUCCESS == code) {
1649
0
    code = setConditionsSlotId(pCxt, (const SLogicNode*)pScanLogicNode, (SPhysiNode*)pScanPhysiNode);
1650
0
  }
1651
1652
0
  if (TSDB_CODE_SUCCESS == code) {
1653
0
    code = setTableScanPrimaryCondSlotId(pCxt, pScanLogicNode, pScanPhysiNode);
1654
0
  }
1655
1656
0
  if (TSDB_CODE_SUCCESS == code) {
1657
0
    pScanPhysiNode->uid = pScanLogicNode->tableId;
1658
0
    pScanPhysiNode->suid = pScanLogicNode->stableId;
1659
0
    pScanPhysiNode->tableType = pScanLogicNode->tableType;
1660
0
    pScanPhysiNode->groupOrderScan = pScanLogicNode->groupOrderScan;
1661
0
    pScanPhysiNode->virtualStableScan = pScanLogicNode->virtualStableScan;
1662
0
    pScanPhysiNode->outputTsPrecision = pScanLogicNode->outputTsPrecision;
1663
0
    pScanPhysiNode->useTriggerTableList = pScanLogicNode->phTbnameScan;
1664
0
    memcpy(&pScanPhysiNode->tableName, &pScanLogicNode->tableName, sizeof(SName));
1665
0
    nodesDestroyNode(pSubplan->pTagCond);
1666
0
    pSubplan->pTagCond = NULL;
1667
0
    if (NULL != pScanLogicNode->pTagCond) {
1668
0
      code = nodesCloneNode(pScanLogicNode->pTagCond, &pSubplan->pTagCond);
1669
0
    }
1670
0
  }
1671
1672
0
  if (TSDB_CODE_SUCCESS == code) {
1673
0
    nodesDestroyNode(pSubplan->pTagIndexCond);
1674
0
    pSubplan->pTagIndexCond = NULL;
1675
0
    if (NULL != pScanLogicNode->pTagIndexCond) {
1676
0
      code = nodesCloneNode(pScanLogicNode->pTagIndexCond, &pSubplan->pTagIndexCond);
1677
0
    }
1678
0
  }
1679
1680
0
  if (TSDB_CODE_SUCCESS == code) {
1681
0
    *pPhyNode = (SPhysiNode*)pScanPhysiNode;
1682
0
  } else {
1683
0
    nodesDestroyNode((SNode*)pScanPhysiNode);
1684
0
  }
1685
1686
0
  return code;
1687
0
}
1688
1689
0
static void vgroupInfoToNodeAddr(const SVgroupInfo* vg, SQueryNodeAddr* pNodeAddr) {
1690
0
  pNodeAddr->nodeId = vg->vgId;
1691
0
  pNodeAddr->epSet = vg->epSet;
1692
0
}
1693
1694
0
static ENodeType getScanOperatorType(EScanType scanType) {
1695
0
  switch (scanType) {
1696
0
    case SCAN_TYPE_TAG:
1697
0
      return QUERY_NODE_PHYSICAL_PLAN_TAG_SCAN;
1698
0
    case SCAN_TYPE_TABLE:
1699
0
      return QUERY_NODE_PHYSICAL_PLAN_TABLE_SCAN;
1700
0
    case SCAN_TYPE_STREAM:
1701
0
      return QUERY_NODE_PHYSICAL_PLAN_STREAM_SCAN;
1702
0
    case SCAN_TYPE_TABLE_MERGE:
1703
0
      return QUERY_NODE_PHYSICAL_PLAN_TABLE_MERGE_SCAN;
1704
0
    case SCAN_TYPE_BLOCK_INFO:
1705
0
      return QUERY_NODE_PHYSICAL_PLAN_BLOCK_DIST_SCAN;
1706
0
    case SCAN_TYPE_TABLE_COUNT:
1707
0
      return QUERY_NODE_PHYSICAL_PLAN_TABLE_COUNT_SCAN;
1708
0
    default:
1709
0
      break;
1710
0
  }
1711
0
  return QUERY_NODE_PHYSICAL_PLAN_TABLE_SCAN;
1712
0
}
1713
1714
static int32_t createSimpleScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan, SScanLogicNode* pScanLogicNode,
1715
0
                                         SPhysiNode** pPhyNode) {
1716
0
  SScanPhysiNode* pScan =
1717
0
      (SScanPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pScanLogicNode, getScanOperatorType(pScanLogicNode->scanType));
1718
0
  if (NULL == pScan) {
1719
0
    return terrno;
1720
0
  }
1721
1722
0
  if (pScanLogicNode->pVgroupList) {
1723
0
    vgroupInfoToNodeAddr(pScanLogicNode->pVgroupList->vgroups, &pSubplan->execNode);
1724
0
  }
1725
0
  return createScanPhysiNodeFinalize(pCxt, pSubplan, pScanLogicNode, pScan, pPhyNode);
1726
0
}
1727
1728
static int32_t createTagScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan, SScanLogicNode* pScanLogicNode,
1729
0
                                      SPhysiNode** pPhyNode) {
1730
0
  STagScanPhysiNode* pScan =
1731
0
      (STagScanPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pScanLogicNode, QUERY_NODE_PHYSICAL_PLAN_TAG_SCAN);
1732
0
  if (NULL == pScan) {
1733
0
    return terrno;
1734
0
  }
1735
0
  if (pScanLogicNode->pVgroupList) {
1736
0
    vgroupInfoToNodeAddr(pScanLogicNode->pVgroupList->vgroups, &pSubplan->execNode);
1737
0
  }
1738
0
  pScan->onlyMetaCtbIdx = pScanLogicNode->onlyMetaCtbIdx;
1739
1740
0
  return createScanPhysiNodeFinalize(pCxt, pSubplan, pScanLogicNode, (SScanPhysiNode*)pScan, pPhyNode);
1741
0
}
1742
1743
0
static bool canBatchLastRowScanRead(const SScanLogicNode* pScan) {
1744
0
  return NULL != pScan && SCAN_TYPE_LAST_ROW == pScan->scanType;
1745
0
}
1746
1747
static int32_t createLastRowScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan, SScanLogicNode* pScanLogicNode,
1748
0
                                          SPhysiNode** pPhyNode) {
1749
0
  SLastRowScanPhysiNode* pScan =
1750
0
      (SLastRowScanPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pScanLogicNode, QUERY_NODE_PHYSICAL_PLAN_LAST_ROW_SCAN);
1751
0
  if (NULL == pScan) {
1752
0
    return terrno;
1753
0
  }
1754
0
  pScan->pTargets = NULL;
1755
0
  int32_t code = nodesCloneList(pScanLogicNode->node.pTargets, &pScan->pTargets);
1756
0
  if (TSDB_CODE_SUCCESS != code) {
1757
0
    nodesDestroyNode((SNode*)pScan);
1758
0
    return code;
1759
0
  }
1760
0
  pScan->pGroupTags = NULL;
1761
0
  code = nodesCloneList(pScanLogicNode->pGroupTags, &pScan->pGroupTags);
1762
0
  if (TSDB_CODE_SUCCESS != code) {
1763
0
    nodesDestroyNode((SNode*)pScan);
1764
0
    return code;
1765
0
  }
1766
1767
0
  pScan->groupSort = pScanLogicNode->groupSort;
1768
0
  pScan->ignoreNull = pScanLogicNode->igLastNull;
1769
0
  pScan->batchMultiTableRead = canBatchLastRowScanRead(pScanLogicNode);
1770
1771
0
  if (pScanLogicNode->pVgroupList) {
1772
0
    vgroupInfoToNodeAddr(pScanLogicNode->pVgroupList->vgroups, &pSubplan->execNode);
1773
0
  }
1774
1775
0
  code = createScanPhysiNodeFinalize(pCxt, pSubplan, pScanLogicNode, (SScanPhysiNode*)pScan, pPhyNode);
1776
0
  if (TSDB_CODE_SUCCESS == code && pScanLogicNode->pFuncTypes != NULL) {
1777
0
    pScan->pFuncTypes = taosArrayInit(taosArrayGetSize(pScanLogicNode->pFuncTypes), sizeof(int32_t));
1778
0
    if (NULL == pScan->pFuncTypes) {
1779
0
      return terrno;
1780
0
    }
1781
1782
0
    SNode* pTargetNode = NULL;
1783
0
    int    funcTypeIndex = 0;
1784
0
    FOREACH(pTargetNode, ((SScanPhysiNode*)pScan)->pScanCols) {
1785
0
      if (((STargetNode*)pTargetNode)->pExpr->type != QUERY_NODE_COLUMN) {
1786
0
        continue;
1787
0
      }
1788
0
      SColumnNode* pColNode = (SColumnNode*)((STargetNode*)pTargetNode)->pExpr;
1789
1790
0
      for (int i = 0; i < TARRAY_SIZE(pScanLogicNode->pFuncTypes); ++i) {
1791
0
        SFunctParam* pFunctParam = taosArrayGet(pScanLogicNode->pFuncTypes, i);
1792
0
        if (pColNode->colId == pFunctParam->pCol->colId &&
1793
0
            0 == strncmp(pColNode->colName, pFunctParam->pCol->name, strlen(pColNode->colName))) {
1794
0
          if (NULL == taosArrayInsert(pScan->pFuncTypes, funcTypeIndex, &pFunctParam->type)) {
1795
0
            code = terrno;
1796
0
          }
1797
0
          break;
1798
0
        }
1799
0
      }
1800
0
      funcTypeIndex++;
1801
0
    }
1802
0
  }
1803
0
  return code;
1804
0
}
1805
1806
static int32_t createTableCountScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan,
1807
0
                                             SScanLogicNode* pScanLogicNode, SPhysiNode** pPhyNode) {
1808
  // A dedicated node type carries the row type filter: older vnodes fail loudly on the unknown node
1809
  // type instead of silently ignoring the filter and returning an unfiltered count.
1810
0
  ENodeType nodeType = (0 != pScanLogicNode->tbCountType) ? QUERY_NODE_PHYSICAL_PLAN_TABLE_COUNT_SCAN_BY_TYPE
1811
0
                                                          : QUERY_NODE_PHYSICAL_PLAN_TABLE_COUNT_SCAN;
1812
0
  STableCountScanPhysiNode* pScan =
1813
0
      (STableCountScanPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pScanLogicNode, nodeType);
1814
0
  if (NULL == pScan) {
1815
0
    return terrno;
1816
0
  }
1817
1818
0
  pScan->pGroupTags = NULL;
1819
0
  int32_t code = nodesCloneList(pScanLogicNode->pGroupTags, &pScan->pGroupTags);
1820
0
  if (NULL != pScanLogicNode->pGroupTags && NULL == pScan->pGroupTags) {
1821
0
    nodesDestroyNode((SNode*)pScan);
1822
0
    return code;
1823
0
  }
1824
1825
0
  pScan->groupSort = pScanLogicNode->groupSort;
1826
0
  pScan->tbCountType = pScanLogicNode->tbCountType;
1827
0
  if (pScanLogicNode->pVgroupList) {
1828
0
    vgroupInfoToNodeAddr(pScanLogicNode->pVgroupList->vgroups, &pSubplan->execNode);
1829
0
  }
1830
0
  return createScanPhysiNodeFinalize(pCxt, pSubplan, pScanLogicNode, (SScanPhysiNode*)pScan, pPhyNode);
1831
0
}
1832
1833
static int32_t createTableScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan, SScanLogicNode* pScanLogicNode,
1834
0
                                        SPhysiNode** pPhyNode) {
1835
0
  STableScanPhysiNode* pTableScan = (STableScanPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pScanLogicNode,
1836
0
                                                                        getScanOperatorType(pScanLogicNode->scanType));
1837
0
  if (NULL == pTableScan) {
1838
0
    return terrno;
1839
0
  }
1840
1841
0
  memcpy(pTableScan->scanSeq, pScanLogicNode->scanSeq, sizeof(pScanLogicNode->scanSeq));
1842
0
  pTableScan->twoPassRescanSource = pScanLogicNode->twoPassRescanSource;
1843
0
  pTableScan->scanRange = pScanLogicNode->scanRange;
1844
0
  TSWAP(pTableScan->pExtScanRange, pScanLogicNode->pExtScanRange);
1845
0
  pTableScan->interpMode = pScanLogicNode->interpMode;
1846
0
  pTableScan->interpFillMode = pScanLogicNode->interpFillMode;
1847
0
  pTableScan->ratio = pScanLogicNode->ratio;
1848
0
  if (pScanLogicNode->pVgroupList) {
1849
0
    vgroupInfoToNodeAddr(pScanLogicNode->pVgroupList->vgroups, &pSubplan->execNode);
1850
0
    pSubplan->execNodeStat.tableNum = pScanLogicNode->pVgroupList->vgroups[0].numOfTable;
1851
0
  }
1852
0
  (void)tNameGetFullDbName(&pScanLogicNode->tableName, pSubplan->dbFName);
1853
0
  pTableScan->dataRequired = pScanLogicNode->dataRequired;
1854
0
  pTableScan->lastOnlyScan = pScanLogicNode->lastOnlyScan;
1855
0
  pTableScan->pDynamicScanFuncs = NULL;
1856
0
  int32_t code = nodesCloneList(pScanLogicNode->pDynamicScanFuncs, &pTableScan->pDynamicScanFuncs);
1857
0
  if (TSDB_CODE_SUCCESS != code) {
1858
0
    nodesDestroyNode((SNode*)pTableScan);
1859
0
    return code;
1860
0
  }
1861
0
  pTableScan->pGroupTags = NULL;
1862
0
  code = nodesCloneList(pScanLogicNode->pGroupTags, &pTableScan->pGroupTags);
1863
0
  if (TSDB_CODE_SUCCESS != code) {
1864
0
    nodesDestroyNode((SNode*)pTableScan);
1865
0
    return code;
1866
0
  }
1867
0
  code = nodesCloneNode(pScanLogicNode->pTimeRange, &pTableScan->pTimeRange);
1868
0
  if (TSDB_CODE_SUCCESS != code) {
1869
0
    nodesDestroyNode((SNode*)pTableScan);
1870
0
    return code;
1871
0
  }
1872
0
  code = nodesCloneNode(pScanLogicNode->pExtTimeRange, &pTableScan->pExtTimeRange);
1873
0
  if (TSDB_CODE_SUCCESS != code) {
1874
0
    nodesDestroyNode((SNode*)pTableScan);
1875
0
    return code;
1876
0
  }
1877
0
  pTableScan->groupSort = pScanLogicNode->groupSort;
1878
0
  pTableScan->interval = pScanLogicNode->interval;
1879
0
  pTableScan->offset = pScanLogicNode->offset;
1880
0
  pTableScan->sliding = pScanLogicNode->sliding;
1881
0
  pTableScan->intervalUnit = pScanLogicNode->intervalUnit;
1882
0
  pTableScan->slidingUnit = pScanLogicNode->slidingUnit;
1883
0
  pTableScan->fastInterval = pScanLogicNode->fastInterval;
1884
0
  pTableScan->exInterval = pScanLogicNode->exInterval;
1885
0
  pTableScan->firstDayOfWeek = pScanLogicNode->firstDayOfWeek;
1886
0
  pTableScan->timezone = pScanLogicNode->timezone;
1887
0
  tstrncpy(pTableScan->timezoneName, pScanLogicNode->timezoneName, sizeof(pTableScan->timezoneName));
1888
0
  pTableScan->triggerType = pScanLogicNode->triggerType;
1889
0
  pTableScan->watermark = pScanLogicNode->watermark;
1890
0
  pTableScan->igExpired = pScanLogicNode->igExpired;
1891
0
  pTableScan->igCheckUpdate = pScanLogicNode->igCheckUpdate;
1892
0
  pTableScan->assignBlockUid = pCxt->pPlanCxt->rSmaQuery ? true : false;
1893
0
  pTableScan->filesetDelimited = pScanLogicNode->filesetDelimited;
1894
0
  pTableScan->needCountEmptyTable = pScanLogicNode->isCountByTag;
1895
0
  pTableScan->paraTablesSort = pScanLogicNode->paraTablesSort;
1896
0
  pTableScan->smallDataTsSort = pScanLogicNode->smallDataTsSort;
1897
1898
0
  return createScanPhysiNodeFinalize(pCxt, pSubplan, pScanLogicNode, (SScanPhysiNode*)pTableScan, pPhyNode);;
1899
0
}
1900
1901
static int32_t createSystemTableScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan,
1902
0
                                              SScanLogicNode* pScanLogicNode, SPhysiNode** pPhyNode) {
1903
0
  SSystemTableScanPhysiNode* pScan = (SSystemTableScanPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pScanLogicNode,
1904
0
                                                                               QUERY_NODE_PHYSICAL_PLAN_SYSTABLE_SCAN);
1905
0
  if (NULL == pScan) {
1906
0
    return terrno;
1907
0
  }
1908
1909
0
  pSubplan->showRewrite = pScanLogicNode->showRewrite;
1910
0
  pScan->showRewrite = pScanLogicNode->showRewrite;
1911
0
  pScan->xnodeDsnOutputMode = pScanLogicNode->xnodeDsnOutputMode;
1912
0
  pScan->accountId = pCxt->pPlanCxt->acctId;
1913
0
  pScan->sysInfo = pCxt->pPlanCxt->sysInfo;
1914
0
  pScan->privInfo = pCxt->pPlanCxt->privInfo;
1915
  // Bind the connection's effective charset so the executor renders NCHAR/JSON
1916
  // system-table tag values in the client charset. taosConvCharset resolves a
1917
  // NULL context to the local process default (client-side plans carry the
1918
  // client charset; server-built plans carry the server charset), so serialize
1919
  // it unconditionally; only genuinely old plans lack the field and fall back to
1920
  // the server default at decode.
1921
0
  tstrncpy(pScan->charsetName, taosConvCharset(pCxt->pPlanCxt->charsetCxt),
1922
0
           TD_CHARSET_LEN);
1923
0
  if (0 == strcmp(pScanLogicNode->tableName.tname, TSDB_INS_TABLE_TABLES) ||
1924
0
      0 == strcmp(pScanLogicNode->tableName.tname, TSDB_INS_TABLE_TAGS) ||
1925
0
      0 == strcmp(pScanLogicNode->tableName.tname, TSDB_INS_TABLE_COLS) ||
1926
0
      0 == strcmp(pScanLogicNode->tableName.tname, TSDB_INS_DISK_USAGE) ||
1927
0
      0 == strcmp(pScanLogicNode->tableName.tname, TSDB_INS_TABLE_LAST_CACHE_STATS) ||
1928
0
      0 == strcmp(pScanLogicNode->tableName.tname, TSDB_INS_TABLE_FILESETS) ||
1929
0
      0 == strcmp(pScanLogicNode->tableName.tname, TSDB_INS_TABLE_VC_COLS) ||
1930
0
      0 == strcmp(pScanLogicNode->tableName.tname, TSDB_INS_TABLE_VIRTUAL_TABLES_REFERENCING) ||
1931
0
      0 == strcmp(pScanLogicNode->tableName.tname, TSDB_INS_TABLE_TABLE_FIXED_DISTRIBUTED)) {
1932
0
    if (pScanLogicNode->pVgroupList) {
1933
0
      vgroupInfoToNodeAddr(pScanLogicNode->pVgroupList->vgroups, &pSubplan->execNode);
1934
0
    }
1935
0
  } else {
1936
0
    pSubplan->execNode.nodeId = MNODE_HANDLE;
1937
0
    pSubplan->execNode.epSet = pCxt->pPlanCxt->mgmtEpSet;
1938
0
  }
1939
0
  if ((0 == strcmp(pScanLogicNode->tableName.tname, TSDB_INS_TABLE_DNODE_VARIABLES) ||
1940
0
       0 == strcmp(pScanLogicNode->tableName.tname, TSDB_INS_TABLE_CPU_ALLOCATION)) &&
1941
0
      pScanLogicNode->pVgroupList) {
1942
0
    pScan->mgmtEpSet = pScanLogicNode->pVgroupList->vgroups->epSet;
1943
0
  } else {
1944
0
    pScan->mgmtEpSet = pCxt->pPlanCxt->mgmtEpSet;
1945
0
  }
1946
0
  (void)tNameGetFullDbName(&pScanLogicNode->tableName, pSubplan->dbFName);
1947
1948
0
  return createScanPhysiNodeFinalize(pCxt, pSubplan, pScanLogicNode, (SScanPhysiNode*)pScan, pPhyNode);
1949
0
}
1950
1951
static int32_t createStreamScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan, SScanLogicNode* pScanLogicNode,
1952
0
                                         SPhysiNode** pPhyNode) {
1953
0
  return createTableScanPhysiNode(pCxt, pSubplan, pScanLogicNode, pPhyNode);
1954
0
}
1955
1956
static int32_t createTableMergeScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan,
1957
0
                                             SScanLogicNode* pScanLogicNode, SPhysiNode** pPhyNode) {
1958
0
  return createTableScanPhysiNode(pCxt, pSubplan, pScanLogicNode, pPhyNode);
1959
0
}
1960
1961
// ---------------------------------------------------------------------------
1962
// --- Remote-plan helpers ----------------------------------------------------
1963
// Convert one logic node from pRemoteLogicPlan into its lightweight physi
1964
// counterpart and wire pChild as its single child.
1965
// pLeaf is passed so LIMIT can be propagated down to the leaf WHERE node.
1966
static int32_t remoteLogicNodeToPhysi(SLogicNode* pLogicNode, SPhysiNode* pChild,
1967
0
                                      SFederatedScanPhysiNode* pLeaf, SPhysiNode** pOut) {
1968
0
  int32_t     code  = TSDB_CODE_SUCCESS;
1969
0
  ENodeType   type  = nodeType(pLogicNode);
1970
0
  SPhysiNode* pPhys = NULL;
1971
0
1972
0
  if (type == QUERY_NODE_LOGIC_PLAN_SORT) {
1973
0
    SSortLogicNode* pSortLogic = (SSortLogicNode*)pLogicNode;
1974
0
    SSortPhysiNode* pSort      = NULL;
1975
0
    code = nodesMakeNode(QUERY_NODE_PHYSICAL_PLAN_SORT, (SNode**)&pSort);
1976
0
    if (TSDB_CODE_SUCCESS != code) return code;
1977
0
1978
0
    code = nodesCloneList(pSortLogic->pSortKeys, &pSort->pSortKeys);
1979
0
    if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pSort); return code; }
1980
0
1981
0
    // Clone pTargets so the executor can derive output column info from the
1982
0
    // topmost remote physical node (used for pColTypeMappings reconstruction).
1983
0
    code = nodesCloneList(pSortLogic->node.pTargets, &pSort->pTargets);
1984
0
    if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pSort); return code; }
1985
0
1986
0
    if (NULL == pLeaf->node.pLimit && NULL != pSortLogic->node.pLimit) {
1987
0
      code = nodesCloneNode(pSortLogic->node.pLimit, &pLeaf->node.pLimit);
1988
0
      if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pSort); return code; }
1989
0
    }
1990
0
    code = nodesMakeList(&pSort->node.pChildren);
1991
0
    if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pSort); return code; }
1992
0
    code = nodesListStrictAppend(pSort->node.pChildren, (SNode*)pChild);
1993
0
    if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pSort); return code; }
1994
0
    pChild->pParent = (SPhysiNode*)pSort;
1995
0
    pPhys = (SPhysiNode*)pSort;
1996
0
1997
0
  } else if (type == QUERY_NODE_LOGIC_PLAN_PROJECT) {
1998
0
    SProjectLogicNode* pProjLogic = (SProjectLogicNode*)pLogicNode;
1999
0
    SProjectPhysiNode* pProj      = NULL;
2000
0
    code = nodesMakeNode(QUERY_NODE_PHYSICAL_PLAN_PROJECT, (SNode**)&pProj);
2001
0
    if (TSDB_CODE_SUCCESS != code) return code;
2002
0
2003
0
    code = nodesCloneList(pProjLogic->pProjections, &pProj->pProjections);
2004
0
    if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pProj); return code; }
2005
0
2006
0
    if (NULL == pLeaf->node.pLimit && NULL != pProjLogic->node.pLimit) {
2007
0
      code = nodesCloneNode(pProjLogic->node.pLimit, &pLeaf->node.pLimit);
2008
0
      if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pProj); return code; }
2009
0
    }
2010
0
    code = nodesMakeList(&pProj->node.pChildren);
2011
0
    if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pProj); return code; }
2012
0
    code = nodesListStrictAppend(pProj->node.pChildren, (SNode*)pChild);
2013
0
    if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pProj); return code; }
2014
0
    pChild->pParent = (SPhysiNode*)pProj;
2015
0
    pPhys = (SPhysiNode*)pProj;
2016
0
2017
0
  } else {
2018
0
    planError("remoteLogicNodeToPhysi: unsupported logic node type %d in pRemoteLogicPlan", type);
2019
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
2020
0
  }
2021
0
2022
0
  *pOut = pPhys;
2023
0
  return TSDB_CODE_SUCCESS;
2024
0
}
2025
0
static bool fqPhysiExprListAllColumns(const SNodeList* pExprList) {
2026
0
  if (NULL == pExprList) {
2027
0
    return true;
2028
0
  }
2029
0
2030
0
  SNode* pExpr = NULL;
2031
0
  FOREACH(pExpr, pExprList) {
2032
0
    const SNode* pInner = pExpr;
2033
0
    if (nodeType(pInner) == QUERY_NODE_TARGET) {
2034
0
      pInner = ((const STargetNode*)pInner)->pExpr;
2035
0
    }
2036
0
    if (NULL == pInner || nodeType(pInner) != QUERY_NODE_COLUMN) {
2037
0
      return false;
2038
0
    }
2039
0
  }
2040
0
2041
0
  return true;
2042
0
}
2043
2044
0
static bool fqPhysiOutputNeedsRemoteMapping(const SNode* pExprNode) {
2045
0
  if (NULL == pExprNode) {
2046
0
    return false;
2047
0
  }
2048
0
2049
0
  const SNode* pExpr = pExprNode;
2050
0
  if (QUERY_NODE_TARGET == nodeType(pExpr)) {
2051
0
    pExpr = ((const STargetNode*)pExpr)->pExpr;
2052
0
  }
2053
0
2054
0
  // Folded constant outputs (VALUE) do not need remote data mapping.
2055
0
  // Remote side may execute SELECT 1 as row anchor, while the executor
2056
0
  // overwrites output slots from VALUE expressions locally.
2057
0
  return pExpr != NULL && nodeType(pExpr) != QUERY_NODE_VALUE;
2058
0
}
2059
2060
0
static void fqCopyTimezoneNameOnly(char* pDst, int32_t dstSize, const char* pSrc) {
2061
0
  if (pDst == NULL || dstSize <= 0) {
2062
0
    return;
2063
0
  }
2064
0
  pDst[0] = '\0';
2065
0
  if (pSrc == NULL || pSrc[0] == '\0') {
2066
0
    return;
2067
0
  }
2068
2069
0
  const char* pSpace = strchr(pSrc, ' ');
2070
0
  int32_t     len = pSpace ? (int32_t)(pSpace - pSrc) : (int32_t)strlen(pSrc);
2071
0
  if (len <= 0) {
2072
0
    return;
2073
0
  }
2074
0
  if (len >= dstSize) {
2075
0
    len = dstSize - 1;
2076
0
  }
2077
0
  memcpy(pDst, pSrc, len);
2078
0
  pDst[len] = '\0';
2079
0
}
2080
2081
0
static void fqSetFederatedScanTimezone(const SPhysiPlanContext* pCxt, SFederatedScanPhysiNode* pScan) {
2082
0
  if (pCxt == NULL || pScan == NULL) {
2083
0
    return;
2084
0
  }
2085
2086
0
  const SPlanContext* pPlanCxt = pCxt->pPlanCxt;
2087
0
  if (pPlanCxt != NULL && pPlanCxt->timezoneExplicit && pPlanCxt->timezoneName[0] != '\0') {
2088
0
    tstrncpy(pScan->timezone, pPlanCxt->timezoneName, sizeof(pScan->timezone));
2089
0
    return;
2090
0
  }
2091
2092
0
  fqCopyTimezoneNameOnly(pScan->timezone, sizeof(pScan->timezone), tsTimezoneStr);
2093
0
  if (pScan->timezone[0] != '\0') {
2094
0
    return;
2095
0
  }
2096
2097
0
  if (pPlanCxt != NULL && pPlanCxt->timezoneName[0] != '\0') {
2098
0
    tstrncpy(pScan->timezone, pPlanCxt->timezoneName, sizeof(pScan->timezone));
2099
0
    return;
2100
0
  }
2101
2102
0
  char sysTz[TD_TIMEZONE_LEN] = {0};
2103
0
  (void)taosGetSystemTimezone(sysTz);
2104
0
  fqCopyTimezoneNameOnly(pScan->timezone, sizeof(pScan->timezone), sysTz);
2105
0
}
2106
2107
0
static SNodeList* fqFindRemoteProjectOutputs(SLogicNode* pRoot) {
2108
0
  SLogicNode* pCur = pRoot;
2109
0
  while (pCur != NULL) {
2110
0
    if (QUERY_NODE_LOGIC_PLAN_PROJECT == nodeType((SNode*)pCur)) {
2111
0
      SNodeList* pProj = ((SProjectLogicNode*)pCur)->pProjections;
2112
0
      if (pProj != NULL && LIST_LENGTH(pProj) > 0) {
2113
0
        return pProj;
2114
0
      }
2115
0
    }
2116
0
2117
0
    if (NULL == pCur->pChildren || LIST_LENGTH(pCur->pChildren) != 1) {
2118
0
      break;
2119
0
    }
2120
0
    pCur = (SLogicNode*)nodesListGetNode(pCur->pChildren, 0);
2121
0
  }
2122
0
2123
0
  return NULL;
2124
0
}
2125
2126
0
static void fqFormatLogicColList(const SNodeList* pCols, char* pBuf, int32_t bufLen) {
2127
0
  if (NULL == pBuf || bufLen <= 0) {
2128
0
    return;
2129
0
  }
2130
2131
0
  pBuf[0] = '\0';
2132
0
  if (NULL == pCols) {
2133
0
    tstrncpy(pBuf, "(NULL)", (size_t)bufLen);
2134
0
    return;
2135
0
  }
2136
2137
0
  int32_t off = 0;
2138
0
  SNode* pCol = NULL;
2139
0
  FOREACH(pCol, pCols) {
2140
0
    const char* name = "?";
2141
0
    if (QUERY_NODE_COLUMN == nodeType(pCol)) {
2142
0
      name = ((const SColumnNode*)pCol)->colName;
2143
0
    } else if (QUERY_NODE_TARGET == nodeType(pCol)) {
2144
0
      const SNode* pExpr = ((const STargetNode*)pCol)->pExpr;
2145
0
      if (pExpr != NULL && QUERY_NODE_COLUMN == nodeType(pExpr)) {
2146
0
        name = ((const SColumnNode*)pExpr)->colName;
2147
0
      }
2148
0
    }
2149
2150
0
    off += snprintf(pBuf + off, (size_t)(bufLen - off), "%s%s", off > 0 ? "," : "", name);
2151
0
    if (off >= bufLen - 1) {
2152
0
      break;
2153
0
    }
2154
0
  }
2155
2156
0
  if ('\0' == pBuf[0]) {
2157
0
    tstrncpy(pBuf, "(EMPTY)", (size_t)bufLen);
2158
0
  }
2159
0
}
2160
2161
0
static int32_t fqReplaceLogicTargets(SLogicNode* pNode, const SNodeList* pAlignedCols, const char* pTag) {
2162
0
  if (NULL == pNode || NULL == pAlignedCols) {
2163
0
    return TSDB_CODE_SUCCESS;
2164
0
  }
2165
2166
0
  char beforeBuf[512] = {0};
2167
0
  char afterBuf[512] = {0};
2168
0
  fqFormatLogicColList(pNode->pTargets, beforeBuf, sizeof(beforeBuf));
2169
0
  fqFormatLogicColList(pAlignedCols, afterBuf, sizeof(afterBuf));
2170
2171
0
  SNodeList* pCloned = NULL;
2172
0
  int32_t code = nodesCloneList((SNodeList*)pAlignedCols, &pCloned);
2173
0
  if (TSDB_CODE_SUCCESS != code) {
2174
0
    planError("FQ-RemoteCols: clone aligned targets failed, tag=%s rootType=%d code:0x%x",
2175
0
              NULL != pTag ? pTag : "(null)", (int)nodeType((SNode*)pNode), code);
2176
0
    return code;
2177
0
  }
2178
2179
0
  nodesDestroyList(pNode->pTargets);
2180
0
  pNode->pTargets = pCloned;
2181
0
  planDebug("FQ-RemoteCols: aligned %s targets, nodeType=%d targetsBefore=[%s] targetsAfter=[%s]",
2182
0
            NULL != pTag ? pTag : "(null)", (int)nodeType((SNode*)pNode), beforeBuf, afterBuf);
2183
0
  return TSDB_CODE_SUCCESS;
2184
0
}
2185
2186
0
static SScanLogicNode* fqFindExternalLogicLeafScan(SLogicNode* pNode) {
2187
0
  if (NULL == pNode) {
2188
0
    return NULL;
2189
0
  }
2190
2191
0
  if (QUERY_NODE_LOGIC_PLAN_SCAN == nodeType((SNode*)pNode)) {
2192
0
    SScanLogicNode* pScan = (SScanLogicNode*)pNode;
2193
0
    if (SCAN_TYPE_EXTERNAL == pScan->scanType) {
2194
0
      return pScan;
2195
0
    }
2196
0
  }
2197
2198
0
  if (NULL == pNode->pChildren) {
2199
0
    return NULL;
2200
0
  }
2201
2202
0
  SNode* pChild = NULL;
2203
0
  FOREACH(pChild, pNode->pChildren) {
2204
0
    SScanLogicNode* pFound = fqFindExternalLogicLeafScan((SLogicNode*)pChild);
2205
0
    if (pFound != NULL) {
2206
0
      return pFound;
2207
0
    }
2208
0
  }
2209
2210
0
  return NULL;
2211
0
}
2212
2213
// Build the pRemotePlan physical tree from pScanLogicNode->pRemoteLogicPlan.
2214
// Walks the logic chain top-down (collecting), then converts bottom-up so the
2215
// leaf SFederatedScanPhysiNode ends up at the bottom of the chain.
2216
// On error the entire chain (including pLeaf) is left for the caller to destroy.
2217
//
2218
// UNION ALL support: when the root of pRemoteLogicPlan is a SProjectLogicNode
2219
// with isSetOpProj=true and multiple children, each child branch is converted
2220
// independently (each gets its own Mode-2 FederatedScan leaf cloned from
2221
// pLeaf) and wired as children of a SProjectPhysiNode container.
2222
2223
// Helper: convert a single-chain logic plan into a physi plan chain.
2224
// pChainRoot: root of the chain (Sort or Scan logic node).
2225
// pLeafTemplate: the Mode-1 outer FederatedScanPhysiNode (for cloning Mode-2 leaves).
2226
// pScanLogic: the original SScanLogicNode (for pExtTable, pScanCols info).
2227
// On success, *pOut is the physi chain root; the Mode-2 leaf is at the bottom.
2228
static int32_t buildRemoteBranchPhysi(SLogicNode* pChainRoot,
2229
                                      SFederatedScanPhysiNode* pLeafTemplate,
2230
                                      SScanLogicNode* pScanLogic,
2231
0
                                      SPhysiNode** pOut) {
2232
0
  // Collect chain into array
2233
0
  SArray* pArr = taosArrayInit(4, POINTER_BYTES);
2234
0
  if (NULL == pArr) return terrno;
2235
0
2236
0
  int32_t code = TSDB_CODE_SUCCESS;
2237
0
  SLogicNode* pCurr = pChainRoot;
2238
0
  while (pCurr != NULL) {
2239
0
    // Stop at SScanLogicNode  -  it becomes the leaf, not a logic-to-physi conversion
2240
0
    if (nodeType(pCurr) == QUERY_NODE_LOGIC_PLAN_SCAN) break;
2241
0
    if (NULL == taosArrayPush(pArr, &pCurr)) {
2242
0
      code = terrno;
2243
0
      taosArrayDestroy(pArr);
2244
0
      return code;
2245
0
    }
2246
0
    SNodeList* pChildren = pCurr->pChildren;
2247
0
    pCurr = (pChildren && LIST_LENGTH(pChildren) > 0)
2248
0
                ? (SLogicNode*)nodesListGetNode(pChildren, 0) : NULL;
2249
0
  }
2250
0
2251
0
  // Create the Mode-2 FederatedScan leaf for this branch
2252
0
  SFederatedScanPhysiNode* pBranchLeaf = NULL;
2253
0
  code = nodesMakeNode(QUERY_NODE_PHYSICAL_PLAN_FEDERATED_SCAN, (SNode**)&pBranchLeaf);
2254
0
  if (TSDB_CODE_SUCCESS != code) { taosArrayDestroy(pArr); return code; }
2255
0
2256
0
  // The branch leaf has its own pExtTable, pScanCols, and pConditions
2257
0
  // from the SScanLogicNode at the bottom of this branch's chain.
2258
0
  SScanLogicNode* pBranchScan = NULL;
2259
0
  if (pCurr != NULL && nodeType(pCurr) == QUERY_NODE_LOGIC_PLAN_SCAN) {
2260
0
    pBranchScan = (SScanLogicNode*)pCurr;
2261
0
  }
2262
0
2263
0
  if (pBranchScan != NULL) {
2264
0
    // Clone from the branch's scan node
2265
0
    code = nodesCloneNode(pBranchScan->pExtTableNode, &pBranchLeaf->pExtTable);
2266
0
    if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pBranchLeaf); taosArrayDestroy(pArr); return code; }
2267
0
    code = nodesCloneList(pBranchScan->pScanCols, &pBranchLeaf->pScanCols);
2268
0
    if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pBranchLeaf); taosArrayDestroy(pArr); return code; }
2269
0
    if (pBranchScan->node.pConditions != NULL) {
2270
0
      code = nodesCloneNode(pBranchScan->node.pConditions, &pBranchLeaf->node.pConditions);
2271
0
      if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pBranchLeaf); taosArrayDestroy(pArr); return code; }
2272
0
    }
2273
0
    if (pBranchScan->node.pLimit != NULL) {
2274
0
      code = nodesCloneNode(pBranchScan->node.pLimit, &pBranchLeaf->node.pLimit);
2275
0
      if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pBranchLeaf); taosArrayDestroy(pArr); return code; }
2276
0
    }
2277
0
  } else {
2278
0
    // Fallback: clone from the outer scan's info
2279
0
    code = nodesCloneNode(pScanLogic->pExtTableNode, &pBranchLeaf->pExtTable);
2280
0
    if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pBranchLeaf); taosArrayDestroy(pArr); return code; }
2281
0
    code = nodesCloneList(pScanLogic->pScanCols, &pBranchLeaf->pScanCols);
2282
0
    if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pBranchLeaf); taosArrayDestroy(pArr); return code; }
2283
0
  }
2284
0
2285
0
  pBranchLeaf->pRemotePlan = NULL;  // Mode-2 leaf
2286
0
2287
0
  // Convert chain bottom-up
2288
0
  int32_t n = (int32_t)taosArrayGetSize(pArr);
2289
0
  SPhysiNode* pBottom = (SPhysiNode*)pBranchLeaf;
2290
0
  for (int32_t i = n - 1; i >= 0; i--) {
2291
0
    SLogicNode* pLogic  = *(SLogicNode**)taosArrayGet(pArr, i);
2292
0
    SPhysiNode* pNewTop = NULL;
2293
0
    code = remoteLogicNodeToPhysi(pLogic, pBottom, pBranchLeaf, &pNewTop);
2294
0
    if (TSDB_CODE_SUCCESS != code) {
2295
0
      nodesDestroyNode((SNode*)pBottom);
2296
0
      taosArrayDestroy(pArr);
2297
0
      return code;
2298
0
    }
2299
0
    pBottom = pNewTop;
2300
0
  }
2301
0
2302
0
  taosArrayDestroy(pArr);
2303
0
  *pOut = pBottom;
2304
0
  return TSDB_CODE_SUCCESS;
2305
0
}
2306
2307
static int32_t buildRemotePlanFromLogicPlan(SScanLogicNode* pScanLogic,
2308
                                            SFederatedScanPhysiNode* pLeaf,
2309
0
                                            SPhysiNode** pRemoteRoot) {
2310
0
  if (NULL == pScanLogic->pRemoteLogicPlan) {
2311
0
    *pRemoteRoot = (SPhysiNode*)pLeaf;
2312
0
    return TSDB_CODE_SUCCESS;
2313
0
  }
2314
0
2315
0
  SLogicNode* pLogicRoot = (SLogicNode*)pScanLogic->pRemoteLogicPlan;
2316
0
2317
0
  // -- UNION ALL: multi-child Project with isSetOpProj=true --
2318
0
  if (nodeType(pLogicRoot) == QUERY_NODE_LOGIC_PLAN_PROJECT &&
2319
0
      ((SProjectLogicNode*)pLogicRoot)->isSetOpProj &&
2320
0
      pLogicRoot->pChildren != NULL &&
2321
0
      LIST_LENGTH(pLogicRoot->pChildren) > 1) {
2322
0
    SProjectLogicNode* pSetOpLogic = (SProjectLogicNode*)pLogicRoot;
2323
0
    int32_t code = TSDB_CODE_SUCCESS;
2324
0
2325
0
    // Create container SProjectPhysiNode
2326
0
    SProjectPhysiNode* pUnionPhysi = NULL;
2327
0
    code = nodesMakeNode(QUERY_NODE_PHYSICAL_PLAN_PROJECT, (SNode**)&pUnionPhysi);
2328
0
    if (TSDB_CODE_SUCCESS != code) { *pRemoteRoot = (SPhysiNode*)pLeaf; return code; }
2329
0
2330
0
    // Clone projections
2331
0
    code = nodesCloneList(pSetOpLogic->pProjections, &pUnionPhysi->pProjections);
2332
0
    if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pUnionPhysi); *pRemoteRoot = (SPhysiNode*)pLeaf; return code; }
2333
0
2334
0
    code = nodesMakeList(&pUnionPhysi->node.pChildren);
2335
0
    if (TSDB_CODE_SUCCESS != code) { nodesDestroyNode((SNode*)pUnionPhysi); *pRemoteRoot = (SPhysiNode*)pLeaf; return code; }
2336
0
2337
0
    // Convert each child branch
2338
0
    SNode* pChild = NULL;
2339
0
    FOREACH(pChild, pLogicRoot->pChildren) {
2340
0
      SPhysiNode* pBranchPhysi = NULL;
2341
0
      code = buildRemoteBranchPhysi((SLogicNode*)pChild, pLeaf, pScanLogic, &pBranchPhysi);
2342
0
      if (TSDB_CODE_SUCCESS != code) {
2343
0
        nodesDestroyNode((SNode*)pUnionPhysi);
2344
0
        *pRemoteRoot = (SPhysiNode*)pLeaf;
2345
0
        return code;
2346
0
      }
2347
0
      pBranchPhysi->pParent = (SPhysiNode*)pUnionPhysi;
2348
0
      code = nodesListStrictAppend(pUnionPhysi->node.pChildren, (SNode*)pBranchPhysi);
2349
0
      if (TSDB_CODE_SUCCESS != code) {
2350
0
        nodesDestroyNode((SNode*)pUnionPhysi);
2351
0
        *pRemoteRoot = (SPhysiNode*)pLeaf;
2352
0
        return code;
2353
0
      }
2354
0
    }
2355
0
2356
0
    *pRemoteRoot = (SPhysiNode*)pUnionPhysi;
2357
0
    return TSDB_CODE_SUCCESS;
2358
0
  }
2359
0
2360
0
  // -- Single-chain: existing behavior --
2361
0
  // Collect chain top-down into a temporary array.
2362
0
  SArray* pArr = taosArrayInit(4, POINTER_BYTES);
2363
0
  if (NULL == pArr) {
2364
0
    *pRemoteRoot = (SPhysiNode*)pLeaf;
2365
0
    return terrno;
2366
0
  }
2367
0
2368
0
  int32_t code  = TSDB_CODE_SUCCESS;
2369
0
  SNode*  pCurr = pScanLogic->pRemoteLogicPlan;
2370
0
  while (pCurr != NULL) {
2371
0
    if (NULL == taosArrayPush(pArr, &pCurr)) {
2372
0
      code = terrno;
2373
0
      *pRemoteRoot = (SPhysiNode*)pLeaf;
2374
0
      goto _done;
2375
0
    }
2376
0
    SNodeList* pChildren = ((SLogicNode*)pCurr)->pChildren;
2377
0
    pCurr = (pChildren && LIST_LENGTH(pChildren) > 0) ? nodesListGetNode(pChildren, 0) : NULL;
2378
0
  }
2379
0
2380
0
  // Convert bottom-up: start with pLeaf as the initial "current bottom".
2381
0
  {
2382
0
    SPhysiNode* pBottom = (SPhysiNode*)pLeaf;
2383
0
    int32_t     n       = (int32_t)taosArrayGetSize(pArr);
2384
0
    for (int32_t i = n - 1; i >= 0; i--) {
2385
0
      SLogicNode* pLogic  = *(SLogicNode**)taosArrayGet(pArr, i);
2386
0
      SPhysiNode* pNewTop = NULL;
2387
0
      code = remoteLogicNodeToPhysi(pLogic, pBottom, pLeaf, &pNewTop);
2388
0
      if (TSDB_CODE_SUCCESS != code) {
2389
0
        // On error, pBottom owns the partial chain (including pLeaf at the
2390
0
        // bottom).  Let the caller destroy it via pRemoteRoot.
2391
0
        *pRemoteRoot = pBottom;
2392
0
        goto _done;
2393
0
      }
2394
0
      pBottom = pNewTop;
2395
0
    }
2396
0
    *pRemoteRoot = pBottom;
2397
0
  }
2398
0
2399
0
_done:
2400
0
  taosArrayDestroy(pArr);
2401
0
  return code;
2402
0
}
2403
static int32_t fqAlignRemotePassthroughOutputsImpl(SLogicNode* pNode, const SNodeList* pAlignedCols,
2404
0
                                                   bool* pPassthrough) {
2405
0
  if (NULL != pPassthrough) {
2406
0
    *pPassthrough = false;
2407
0
  }
2408
0
  if (NULL == pNode || NULL == pAlignedCols) {
2409
0
    return TSDB_CODE_SUCCESS;
2410
0
  }
2411
2412
0
  if (QUERY_NODE_LOGIC_PLAN_SCAN == nodeType((SNode*)pNode)) {
2413
0
    SScanLogicNode* pScan = (SScanLogicNode*)pNode;
2414
0
    if (SCAN_TYPE_EXTERNAL != pScan->scanType) {
2415
0
      return TSDB_CODE_SUCCESS;
2416
0
    }
2417
2418
0
    char beforeBuf[512] = {0};
2419
0
    char afterBuf[512] = {0};
2420
0
    fqFormatLogicColList(pScan->pScanCols, beforeBuf, sizeof(beforeBuf));
2421
0
    fqFormatLogicColList(pAlignedCols, afterBuf, sizeof(afterBuf));
2422
2423
0
    SNodeList* pCloned = NULL;
2424
0
    int32_t code = nodesCloneList((SNodeList*)pAlignedCols, &pCloned);
2425
0
    if (TSDB_CODE_SUCCESS != code) {
2426
0
      planError("FQ-RemoteCols: clone aligned scan cols failed, remoteRootType=%d code:0x%x",
2427
0
                (int)nodeType((SNode*)pNode), code);
2428
0
      return code;
2429
0
    }
2430
2431
0
    nodesDestroyList(pScan->pScanCols);
2432
0
    pScan->pScanCols = pCloned;
2433
0
    planDebug("FQ-RemoteCols: aligned remote leaf scan cols, remoteRootType=%d leafBefore=[%s] leafAfter=[%s]",
2434
0
              (int)nodeType((SNode*)pNode), beforeBuf, afterBuf);
2435
2436
0
    code = fqReplaceLogicTargets((SLogicNode*)pScan, pAlignedCols, "leaf-scan");
2437
0
    if (TSDB_CODE_SUCCESS != code) {
2438
0
      return code;
2439
0
    }
2440
2441
0
    if (NULL != pPassthrough) {
2442
0
      *pPassthrough = true;
2443
0
    }
2444
0
    return TSDB_CODE_SUCCESS;
2445
0
  }
2446
2447
0
  if (QUERY_NODE_LOGIC_PLAN_SORT == nodeType((SNode*)pNode) &&
2448
0
      NULL != pNode->pChildren && 1 == LIST_LENGTH(pNode->pChildren)) {
2449
0
    bool childPassthrough = false;
2450
0
    int32_t code =
2451
0
        fqAlignRemotePassthroughOutputsImpl((SLogicNode*)nodesListGetNode(pNode->pChildren, 0),
2452
0
                                            pAlignedCols, &childPassthrough);
2453
0
    if (TSDB_CODE_SUCCESS != code) {
2454
0
      return code;
2455
0
    }
2456
0
    if (childPassthrough) {
2457
0
      code = fqReplaceLogicTargets(pNode, pAlignedCols, "sort-passthrough");
2458
0
      if (TSDB_CODE_SUCCESS != code) {
2459
0
        return code;
2460
0
      }
2461
0
      if (NULL != pPassthrough) {
2462
0
        *pPassthrough = true;
2463
0
      }
2464
0
    } else {
2465
0
      planDebug("FQ-RemoteCols: skip sort target alignment, child is not passthrough, rootType=%d",
2466
0
                (int)nodeType((SNode*)pNode));
2467
0
    }
2468
0
    return TSDB_CODE_SUCCESS;
2469
0
  }
2470
2471
0
  planDebug("FQ-RemoteCols: skip passthrough alignment for remoteRootType=%d", (int)nodeType((SNode*)pNode));
2472
0
  return TSDB_CODE_SUCCESS;
2473
0
}
2474
2475
0
static int32_t fqAlignRemotePassthroughOutputs(SNode* pRemoteRoot, const SNodeList* pAlignedCols) {
2476
0
  if (NULL == pRemoteRoot || NULL == pAlignedCols) {
2477
0
    return TSDB_CODE_SUCCESS;
2478
0
  }
2479
2480
0
  bool passthrough = false;
2481
0
  int32_t code = fqAlignRemotePassthroughOutputsImpl((SLogicNode*)pRemoteRoot, pAlignedCols, &passthrough);
2482
0
  if (TSDB_CODE_SUCCESS != code) {
2483
0
    return code;
2484
0
  }
2485
0
  if (!passthrough) {
2486
0
    SScanLogicNode* pLeaf = fqFindExternalLogicLeafScan((SLogicNode*)pRemoteRoot);
2487
0
    if (NULL == pLeaf) {
2488
0
      planDebug("FQ-RemoteCols: no external logic leaf found for remoteRootType=%d", (int)nodeType(pRemoteRoot));
2489
0
    }
2490
0
  }
2491
0
  return TSDB_CODE_SUCCESS;
2492
0
}
2493
2494
0
static SNodeList* fqGetRemoteOutputTargets(SNode* pRemoteRoot) {
2495
0
  if (NULL == pRemoteRoot) {
2496
0
    return NULL;
2497
0
  }
2498
2499
0
  return ((SLogicNode*)pRemoteRoot)->pTargets;
2500
0
}
2501
2502
0
static int32_t fqRefreshRemoteOutputTargets(SNode* pRemoteRoot) {
2503
0
  if (NULL == pRemoteRoot) {
2504
0
    return TSDB_CODE_SUCCESS;
2505
0
  }
2506
2507
0
  SNodesRemoteSelectOutputs outputs = {0};
2508
0
  int32_t code = nodesCollectRemoteQuerySelectOutputs((SLogicNode*)pRemoteRoot, &outputs);
2509
0
  if (TSDB_CODE_SUCCESS != code) {
2510
0
    planError("FQ-RemoteCols: collect remote select outputs failed, rootType=%d code:0x%x",
2511
0
              (int)nodeType(pRemoteRoot), code);
2512
0
    return code;
2513
0
  }
2514
2515
0
  SNodeList* pNewTargets = NULL;
2516
0
  code = createColumnByRemoteOutputExprs(outputs.pSelectExprs, &pNewTargets);
2517
0
  if (TSDB_CODE_SUCCESS != code) {
2518
0
    nodesDestroyRemoteSelectOutputs(&outputs);
2519
0
    return code;
2520
0
  }
2521
2522
0
  code = fqReplaceLogicTargets((SLogicNode*)pRemoteRoot, pNewTargets, "remote-select-outputs");
2523
0
  nodesDestroyList(pNewTargets);
2524
0
  if (TSDB_CODE_SUCCESS != code) {
2525
0
    nodesDestroyRemoteSelectOutputs(&outputs);
2526
0
    return code;
2527
0
  }
2528
2529
0
  code = replaceProjectLogicProjections((SLogicNode*)pRemoteRoot, outputs.pSelectExprs);
2530
0
  if (TSDB_CODE_SUCCESS != code) {
2531
0
    nodesDestroyRemoteSelectOutputs(&outputs);
2532
0
    return code;
2533
0
  }
2534
2535
0
  planDebug("FQ-RemoteCols: refreshed remote output targets, rootType=%d source=%d keyExprs=%d outputs=%d",
2536
0
            (int)nodeType(pRemoteRoot), (int)outputs.source, outputs.keyExprCount,
2537
0
            outputs.pSelectExprs ? LIST_LENGTH(outputs.pSelectExprs) : 0);
2538
0
  nodesDestroyRemoteSelectOutputs(&outputs);
2539
0
  return TSDB_CODE_SUCCESS;
2540
0
}
2541
2542
0
static SNode* fqResolveRemoteMappingExpr(SNode* pExprNode) {
2543
0
  SNode* pExpr = pExprNode;
2544
0
  if (NULL == pExpr) {
2545
0
    return NULL;
2546
0
  }
2547
2548
0
  if (QUERY_NODE_TARGET == nodeType(pExpr)) {
2549
0
    pExpr = ((STargetNode*)pExpr)->pExpr;
2550
0
  }
2551
2552
0
  if (NULL != pExpr && QUERY_NODE_GROUPING_SET == nodeType(pExpr)) {
2553
0
    pExpr = nodesListGetNode(((SGroupingSetNode*)pExpr)->pParameterList, 0);
2554
0
  }
2555
2556
  // Mapping cardinality and order always follow the final remote output target
2557
  // list exactly.  This helper only unwraps planner-internal wrappers so we can
2558
  // read the concrete result type for that SAME output slot.
2559
0
  return pExpr;
2560
0
}
2561
2562
// The physi federated scan only consumes the TOP-LEVEL remote SELECT output.
2563
// Its column-name mappings therefore must follow the same output-name policy as
2564
// the top-level remote renderer:
2565
//   - current-scope output refs expose userAlias when present, otherwise colName
2566
//   - leaf/external columns keep colName
2567
//   - computed expressions expose userAlias
2568
0
static const char* fqResolveRemoteOutputName(const SNode* pExprNode) {
2569
0
  const SNode* pExpr = pExprNode;
2570
0
  if (NULL == pExpr) {
2571
0
    return NULL;
2572
0
  }
2573
2574
0
  if (QUERY_NODE_TARGET == nodeType(pExpr)) {
2575
0
    pExpr = ((const STargetNode*)pExpr)->pExpr;
2576
0
  }
2577
2578
0
  if (NULL != pExpr && QUERY_NODE_GROUPING_SET == nodeType(pExpr)) {
2579
0
    pExpr = nodesListGetNode(((const SGroupingSetNode*)pExpr)->pParameterList, 0);
2580
0
  }
2581
2582
0
  if (NULL == pExpr || !nodesIsExprNode((SNode*)pExpr)) {
2583
0
    return NULL;
2584
0
  }
2585
2586
0
  const SExprNode* pResExpr = (const SExprNode*)pExpr;
2587
0
  if (QUERY_NODE_COLUMN == nodeType(pExpr)) {
2588
0
    const SColumnNode* pCol = (const SColumnNode*)pExpr;
2589
0
    bool isCurrentScopeOutputRef = ('\0' == pCol->tableAlias[0] && '\0' == pCol->tableName[0]);
2590
0
    if (isCurrentScopeOutputRef) {
2591
0
      if ('\0' != pResExpr->userAlias[0]) {
2592
0
        return pResExpr->userAlias;
2593
0
      }
2594
0
      if ('\0' != pCol->colName[0]) {
2595
0
        return pCol->colName;
2596
0
      }
2597
0
      return NULL;
2598
0
    }
2599
0
    if ('\0' != pCol->colName[0]) {
2600
0
      return pCol->colName;
2601
0
    }
2602
0
  } else {
2603
0
    if ('\0' != pResExpr->userAlias[0]) {
2604
0
      return pResExpr->userAlias;
2605
0
    }
2606
0
  }
2607
2608
0
  return NULL;
2609
0
}
2610
// -----------------------------------------------------------------------------
2611
2612
// fqLogFedScanPhysi - logs the result of createFederatedScanPhysiNode at DEBUG
2613
// level.  Gated by qDebugFlag so no string-building overhead when debug logging
2614
// is off.  Output example:
2615
//   FQ-PHY createFedScan: pScanCols=[time,val] pLeaf.pScanCols=[time,val] pRemoteRoot=Sort numColTypeMappings=2
2616
static void fqLogFedScanPhysi(const SFederatedScanPhysiNode* pScan,
2617
                               const SFederatedScanPhysiNode* pLeaf,
2618
0
                               const SPhysiNode*              pRemoteRoot) {
2619
0
  if (!(qDebugFlag & DEBUG_DEBUG)) return;
2620
0
2621
0
  // pScanCols (outer Mode-1 node)
2622
0
  char scanColsBuf[512] = {0};
2623
0
  int  off = 0;
2624
0
  if (pScan->pScanCols) {
2625
0
    SNode* pSc = NULL;
2626
0
    FOREACH(pSc, pScan->pScanCols) {
2627
0
      const char* cn = "?";
2628
0
      if (nodeType(pSc) == QUERY_NODE_COLUMN) cn = ((const SColumnNode*)pSc)->colName;
2629
0
      else if (nodeType(pSc) == QUERY_NODE_TARGET) {
2630
0
        const SNode* pi = ((const STargetNode*)pSc)->pExpr;
2631
0
        if (pi && nodeType(pi) == QUERY_NODE_COLUMN) cn = ((const SColumnNode*)pi)->colName;
2632
0
      }
2633
0
      off += snprintf(scanColsBuf + off, sizeof(scanColsBuf) - (size_t)off - 1, "%s,", cn);
2634
0
      if (off >= (int)sizeof(scanColsBuf) - 2) break;
2635
0
    }
2636
0
    if (off > 0 && scanColsBuf[off - 1] == ',') scanColsBuf[off - 1] = '\0';
2637
0
  } else {
2638
0
    tstrncpy(scanColsBuf, "(NULL)", sizeof(scanColsBuf));
2639
0
  }
2640
0
2641
0
  // pScanCols (inner Mode-2 leaf - what remote SQL SELECT is built from)
2642
0
  char leafColsBuf[512] = {0};
2643
0
  off = 0;
2644
0
  if (pLeaf->pScanCols) {
2645
0
    SNode* pSc = NULL;
2646
0
    FOREACH(pSc, pLeaf->pScanCols) {
2647
0
      const char* cn = "?";
2648
0
      if (nodeType(pSc) == QUERY_NODE_COLUMN) cn = ((const SColumnNode*)pSc)->colName;
2649
0
      off += snprintf(leafColsBuf + off, sizeof(leafColsBuf) - (size_t)off - 1, "%s,", cn);
2650
0
      if (off >= (int)sizeof(leafColsBuf) - 2) break;
2651
0
    }
2652
0
    if (off > 0 && leafColsBuf[off - 1] == ',') leafColsBuf[off - 1] = '\0';
2653
0
  } else {
2654
0
    tstrncpy(leafColsBuf, "(NULL)", sizeof(leafColsBuf));
2655
0
  }
2656
0
2657
0
  // pRemoteRoot node type
2658
0
  const char* rootTypeName = "(null)";
2659
0
  if (pRemoteRoot) {
2660
0
    ENodeType rnt = nodeType(pRemoteRoot);
2661
0
    rootTypeName = (rnt == QUERY_NODE_PHYSICAL_PLAN_SORT)         ? "Sort" :
2662
0
                   (rnt == QUERY_NODE_PHYSICAL_PLAN_HASH_AGG)     ? "HashAgg" :
2663
0
                   (rnt == QUERY_NODE_PHYSICAL_PLAN_PROJECT)      ? "Project" :
2664
0
                   (rnt == QUERY_NODE_PHYSICAL_PLAN_FEDERATED_SCAN) ? "FedScan(leaf)" : "?";
2665
0
  }
2666
0
2667
0
  planDebug("FQ-PHY createFedScan: pScanCols=[%s] pLeaf.pScanCols=[%s] "
2668
0
            "pRemoteRoot=%s numColTypeMappings=%d",
2669
0
            scanColsBuf, leafColsBuf, rootTypeName,
2670
0
            pScan->numColTypeMappings);
2671
0
}
2672
2673
0
static bool fqTypeStartsWithIgnoreCase(const char* pTypeName, const char* pPrefix) {
2674
0
  if (pTypeName == NULL || pPrefix == NULL) {
2675
0
    return false;
2676
0
  }
2677
2678
0
  size_t prefixLen = strlen(pPrefix);
2679
0
  return prefixLen > 0 && 0 == strncasecmp(pTypeName, pPrefix, prefixLen);
2680
0
}
2681
2682
0
static bool fqTypeContainsIgnoreCase(const char* pTypeName, const char* pNeedle) {
2683
0
  if (pTypeName == NULL || pNeedle == NULL || pNeedle[0] == '\0') {
2684
0
    return false;
2685
0
  }
2686
2687
0
  size_t hayLen = strlen(pTypeName);
2688
0
  size_t needleLen = strlen(pNeedle);
2689
0
  if (needleLen > hayLen) {
2690
0
    return false;
2691
0
  }
2692
2693
0
  for (size_t i = 0; i + needleLen <= hayLen; ++i) {
2694
0
    if (0 == strncasecmp(pTypeName + i, pNeedle, needleLen)) {
2695
0
      return true;
2696
0
    }
2697
0
  }
2698
2699
0
  return false;
2700
0
}
2701
2702
0
static EExtTimestampTzClass fqPlanClassifyTimestampType(int8_t sourceType, const char* pExtTypeName) {
2703
0
  if (pExtTypeName == NULL || pExtTypeName[0] == '\0') {
2704
0
    return EXT_TIMESTAMP_TZ_CLASS_UNKNOWN;
2705
0
  }
2706
2707
0
  switch ((EExtSourceType)sourceType) {
2708
0
    case EXT_SOURCE_MYSQL:
2709
0
      if (fqTypeStartsWithIgnoreCase(pExtTypeName, "timestamp")) {
2710
0
        return EXT_TIMESTAMP_TZ_CLASS_AWARE;
2711
0
      }
2712
0
      if (fqTypeStartsWithIgnoreCase(pExtTypeName, "datetime") ||
2713
0
          fqTypeStartsWithIgnoreCase(pExtTypeName, "date")) {
2714
0
        return EXT_TIMESTAMP_TZ_CLASS_NAIVE;
2715
0
      }
2716
0
      break;
2717
0
    case EXT_SOURCE_POSTGRESQL:
2718
0
      if (fqTypeStartsWithIgnoreCase(pExtTypeName, "timestamptz") ||
2719
0
          fqTypeContainsIgnoreCase(pExtTypeName, "with time zone")) {
2720
0
        return EXT_TIMESTAMP_TZ_CLASS_AWARE;
2721
0
      }
2722
0
      if (fqTypeStartsWithIgnoreCase(pExtTypeName, "timestamp") ||
2723
0
          fqTypeStartsWithIgnoreCase(pExtTypeName, "date")) {
2724
0
        return EXT_TIMESTAMP_TZ_CLASS_NAIVE;
2725
0
      }
2726
0
      break;
2727
0
    case EXT_SOURCE_INFLUXDB:
2728
0
      if (fqTypeStartsWithIgnoreCase(pExtTypeName, "timestamp")) {
2729
0
        return EXT_TIMESTAMP_TZ_CLASS_AWARE;
2730
0
      }
2731
0
      if (fqTypeStartsWithIgnoreCase(pExtTypeName, "date32") ||
2732
0
          fqTypeStartsWithIgnoreCase(pExtTypeName, "date64")) {
2733
0
        return EXT_TIMESTAMP_TZ_CLASS_NAIVE;
2734
0
      }
2735
0
      break;
2736
0
    default:
2737
0
      break;
2738
0
  }
2739
2740
0
  return EXT_TIMESTAMP_TZ_CLASS_UNKNOWN;
2741
0
}
2742
2743
0
static int32_t fqBuildTimestampTypeHints(SFederatedScanPhysiNode* pScan, const SExtTableNode* pExtNode) {
2744
0
  if (pScan == NULL || pExtNode == NULL || pExtNode->pExtMeta == NULL || pExtNode->pExtMeta->pCols == NULL) {
2745
0
    return TSDB_CODE_SUCCESS;
2746
0
  }
2747
2748
0
  int32_t hintCount = 0;
2749
0
  for (int32_t i = 0; i < pExtNode->pExtMeta->numOfCols; ++i) {
2750
0
    if (fqPlanClassifyTimestampType(pExtNode->sourceType, pExtNode->pExtMeta->pCols[i].extTypeName) !=
2751
0
        EXT_TIMESTAMP_TZ_CLASS_UNKNOWN) {
2752
0
      ++hintCount;
2753
0
    }
2754
0
  }
2755
2756
0
  if (hintCount == 0) {
2757
0
    return TSDB_CODE_SUCCESS;
2758
0
  }
2759
2760
0
  pScan->pTimestampTypeHints =
2761
0
      (SExtTimestampTypeHint*)taosMemoryCalloc(hintCount, sizeof(SExtTimestampTypeHint));
2762
0
  if (pScan->pTimestampTypeHints == NULL) {
2763
0
    return TSDB_CODE_OUT_OF_MEMORY;
2764
0
  }
2765
2766
0
  int32_t hintIdx = 0;
2767
0
  for (int32_t i = 0; i < pExtNode->pExtMeta->numOfCols; ++i) {
2768
0
    const SExtColumnDef* pCol = &pExtNode->pExtMeta->pCols[i];
2769
0
    EExtTimestampTzClass tzClass = fqPlanClassifyTimestampType(pExtNode->sourceType, pCol->extTypeName);
2770
0
    if (tzClass == EXT_TIMESTAMP_TZ_CLASS_UNKNOWN) {
2771
0
      continue;
2772
0
    }
2773
2774
0
    tstrncpy(pScan->pTimestampTypeHints[hintIdx].colName, pCol->colName,
2775
0
             sizeof(pScan->pTimestampTypeHints[hintIdx].colName));
2776
0
    tstrncpy(pScan->pTimestampTypeHints[hintIdx].extTypeName, pCol->extTypeName,
2777
0
             sizeof(pScan->pTimestampTypeHints[hintIdx].extTypeName));
2778
0
    pScan->pTimestampTypeHints[hintIdx].tzClass = (int8_t)tzClass;
2779
0
    planDebug("FQ-TsTypeHints[%d]: source=%s col=%s extType=%s tzClass=%d",
2780
0
              hintIdx, pExtNode->sourceName, pCol->colName, pCol->extTypeName, (int)tzClass);
2781
0
    ++hintIdx;
2782
0
  }
2783
2784
0
  pScan->numTimestampTypeHints = hintIdx;
2785
0
  planDebug("FQ-TsTypeHints: source=%s builtHints=%d",
2786
0
            pExtNode->sourceName, pScan->numTimestampTypeHints);
2787
0
  return TSDB_CODE_SUCCESS;
2788
0
}
2789
2790
// PostgreSQL federated scans require an explicit schema at both the planner
2791
// node and connector-config levels. Keep them aligned before executor handoff.
2792
0
static int32_t fqEnsurePgExtSchemaNode(SExtTableNode* pExtNode) {
2793
0
  if (NULL == pExtNode || EXT_SOURCE_POSTGRESQL != pExtNode->sourceType) {
2794
0
    return TSDB_CODE_SUCCESS;
2795
0
  }
2796
2797
0
  if ('\0' == pExtNode->schemaName[0] && '\0' != pExtNode->srcSchema[0]) {
2798
0
    tstrncpy(pExtNode->schemaName, pExtNode->srcSchema, sizeof(pExtNode->schemaName));
2799
0
  } else if ('\0' == pExtNode->srcSchema[0] && '\0' != pExtNode->schemaName[0]) {
2800
0
    tstrncpy(pExtNode->srcSchema, pExtNode->schemaName, sizeof(pExtNode->srcSchema));
2801
0
  }
2802
2803
0
  if ('\0' == pExtNode->schemaName[0] || '\0' == pExtNode->srcSchema[0]) {
2804
0
    planError("FQ-PHY: PostgreSQL schema missing source=%s table=%s schemaName=%s srcSchema=%s",
2805
0
              pExtNode->sourceName,
2806
0
              pExtNode->table.tableName,
2807
0
              '\0' != pExtNode->schemaName[0] ? pExtNode->schemaName : "(empty)",
2808
0
              '\0' != pExtNode->srcSchema[0] ? pExtNode->srcSchema : "(empty)");
2809
0
    return TSDB_CODE_PLAN_INVALID_FEDERATED_SCAN;
2810
0
  }
2811
2812
0
  return TSDB_CODE_SUCCESS;
2813
0
}
2814
2815
static int32_t fqInitFederatedScanSource(SFederatedScanPhysiNode* pScan,
2816
0
                                         const SExtTableNode* pExtNode) {
2817
0
  PLAN_ERR_RET(nodesCloneNode((const SNode*)pExtNode, &pScan->pExtTable));
2818
0
  pScan->sourceType = pExtNode->sourceType;
2819
0
  tstrncpy(pScan->srcHost, pExtNode->srcHost, sizeof(pScan->srcHost));
2820
0
  pScan->srcPort = pExtNode->srcPort;
2821
0
  tstrncpy(pScan->srcUser, pExtNode->srcUser, sizeof(pScan->srcUser));
2822
0
  tstrncpy(pScan->srcPassword, pExtNode->srcPassword, sizeof(pScan->srcPassword));
2823
0
  tstrncpy(pScan->srcDatabase, pExtNode->srcDatabase, sizeof(pScan->srcDatabase));
2824
0
  tstrncpy(pScan->srcSchema, pExtNode->srcSchema, sizeof(pScan->srcSchema));
2825
0
  tstrncpy(pScan->srcOptions, pExtNode->srcOptions, sizeof(pScan->srcOptions));
2826
0
  pScan->metaVersion = pExtNode->metaVersion;
2827
0
  return TSDB_CODE_SUCCESS;
2828
0
}
2829
2830
// Consume required filters only in the remote-plan copy, after optional pushdown.
2831
0
static int32_t fqAttachRequiredScanConditions(SLogicNode* pNode, uint32_t* pPushdownFlags) {
2832
0
  int32_t code = TSDB_CODE_SUCCESS;
2833
0
  int32_t lino = 0;
2834
2835
0
  if (QUERY_NODE_LOGIC_PLAN_SCAN == nodeType(pNode)) {
2836
0
    SScanLogicNode* pScan = (SScanLogicNode*)pNode;
2837
0
    if (NULL != pScan->pPushedConditions) {
2838
0
      if (NULL != pNode->pConditions) {
2839
0
        code = nodesMergeNode(&pScan->pPushedConditions, &pNode->pConditions);
2840
0
        QUERY_CHECK_CODE(code, lino, _return);
2841
0
      }
2842
0
      TSWAP(pNode->pConditions, pScan->pPushedConditions);
2843
0
      *pPushdownFlags |= FQ_PUSHDOWN_REQUIRED_FILTER;
2844
0
    }
2845
0
  }
2846
2847
0
  SNode* pChild = NULL;
2848
0
  FOREACH(pChild, pNode->pChildren) {
2849
0
    code = fqAttachRequiredScanConditions((SLogicNode*)pChild, pPushdownFlags);
2850
0
    QUERY_CHECK_CODE(code, lino, _return);
2851
0
  }
2852
2853
0
_return:
2854
0
  if (TSDB_CODE_SUCCESS != code) {
2855
0
    qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
2856
0
  }
2857
0
  return code;
2858
0
}
2859
2860
// createFederatedScanPhysiNode: builds SFederatedScanPhysiNode from a logic
2861
// node whose scanType == SCAN_TYPE_EXTERNAL.
2862
// ---------------------------------------------------------------------------
2863
static int32_t createFederatedScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan,
2864
0
                                            SScanLogicNode* pScanLogicNode, SPhysiNode** pPhyNode) {
2865
0
  int32_t                     code = TSDB_CODE_SUCCESS;
2866
0
  int32_t                     lino = 0;
2867
0
  SFederatedScanPhysiNode*    pScan = NULL;
2868
0
  SNodeList*                  pReordered = NULL;
2869
0
  SNodeList*                  pReserveSlotCols = NULL;
2870
0
  SNode*                      pRemoteRoot = NULL;
2871
2872
0
  if (NULL == pScanLogicNode->pExtTableNode) {
2873
0
    planError("createFederatedScanPhysiNode: pExtTableNode is NULL");
2874
0
    PLAN_ERR_JRET(TSDB_CODE_PLAN_INVALID_FEDERATED_SCAN);
2875
0
  }
2876
0
  bool isDynamicVstableScan = pScanLogicNode->virtualStableScan && pScanLogicNode->node.dynamicOp;
2877
0
  if (!isDynamicVstableScan &&
2878
0
      (NULL == pScanLogicNode->pScanCols || LIST_LENGTH(pScanLogicNode->pScanCols) == 0)) {
2879
0
    planError("FQ-PHY: logic scan invariant violated, pScanCols empty source=%s table=%s remotePlan=%p",
2880
0
              ((SExtTableNode*)pScanLogicNode->pExtTableNode)->sourceName,
2881
0
              pScanLogicNode->tableName.tname,
2882
0
              pScanLogicNode->pRemoteLogicPlan);
2883
0
    PLAN_ERR_JRET(TSDB_CODE_PLAN_INVALID_FEDERATED_SCAN);
2884
0
  }
2885
0
  if (!isDynamicVstableScan &&
2886
0
      (NULL == pScanLogicNode->node.pTargets || LIST_LENGTH(pScanLogicNode->node.pTargets) == 0)) {
2887
0
    planError("FQ-PHY: logic scan invariant violated, node targets empty source=%s table=%s scanCols=%d",
2888
0
              ((SExtTableNode*)pScanLogicNode->pExtTableNode)->sourceName,
2889
0
              pScanLogicNode->tableName.tname,
2890
0
              (int)LIST_LENGTH(pScanLogicNode->pScanCols));
2891
0
    PLAN_ERR_JRET(TSDB_CODE_PLAN_INVALID_FEDERATED_SCAN);
2892
0
  }
2893
0
  SExtTableNode* pExtNode = (SExtTableNode*)pScanLogicNode->pExtTableNode;
2894
0
  PLAN_ERR_JRET(fqEnsurePgExtSchemaNode(pExtNode));
2895
0
  bool useRemoteLogicPlan =
2896
0
      (NULL != pScanLogicNode->pRemoteLogicPlan) &&
2897
0
      !fqShouldUseAnalyzeInfluxScalarAggFallback(pCxt, pScanLogicNode);
2898
  // Stream %%trows readers materialize source rows and run their calc plan locally.
2899
0
  bool useRemoteResultSchema = useRemoteLogicPlan && !inStreamCalcClause(pCxt->pPlanCxt);
2900
2901
0
  pScan = (SFederatedScanPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pScanLogicNode,
2902
0
                                                  QUERY_NODE_PHYSICAL_PLAN_FEDERATED_SCAN);
2903
0
  if (NULL == pScan) {
2904
0
    planError("createFederatedScanPhysiNode: makePhysiNode OOM");
2905
0
    PLAN_ERR_JRET(terrno);
2906
0
  }
2907
0
  PLAN_ERR_JRET(fqInitFederatedScanSource(pScan, pExtNode));
2908
2909
  // dynamicOp external scan nodes (VStb path) have NULL pTargets/pScanCols;
2910
  // DynQueryCtrl fills them at runtime after the common source initialization.
2911
0
  if (isDynamicVstableScan) {
2912
0
    pScan->scanRange = pScanLogicNode->scanRange;
2913
0
    PLAN_ERR_JRET(nodesCloneList(pScanLogicNode->pScanCols, &pScan->pScanCols));
2914
0
    fqSetFederatedScanTimezone(pCxt, pScan);
2915
0
    pCxt->hasFederatedScan = true;
2916
0
    *pPhyNode = (SPhysiNode*)pScan;
2917
0
    pScan = NULL;
2918
0
    goto _return;
2919
0
  }
2920
2921
  // When pRemoteLogicPlan is set, the remote connector executes the full
2922
  // pushed-down plan and returns exactly the topmost node's output columns
2923
  // (pTargets).  The executor rebuilds pColTypeMappings from the physi-plan
2924
  // topmost node's pTargets (SSortPhysiNode.pTargets / SProjectPhysiNode.pProjections).
2925
  // We do NOT rebuild pOutputDataBlockDesc here because it uses pCxt's
2926
  // dataBlockId registry and downstream slot-ID resolution depends on it.
2927
2928
  // Clone scan columns in pTargets order.
2929
  // makePhysiNode() already built the output data block descriptor from
2930
  // pLogicNode->pTargets (which may have been reordered by
2931
  // eliminateProjOptimize to match the user's SELECT list).  The output desc
2932
  // slot order = pTargets order.  We must ensure pScanCols, pColTypeMappings,
2933
  // and the remote SQL SELECT list all use the same order so that the data
2934
  // block columns returned by the connector match the slot descriptors.
2935
  //
2936
  // pScanCols (from nodesCollectColumns) may differ in order from pTargets
2937
  // because the AST walker visits ORDER BY before PROJECTION.  Reorder the
2938
  // clone to match pTargets.
2939
0
  {
2940
0
    bool       reorderOk  = true;
2941
2942
0
    if (pScanLogicNode->node.pTargets != NULL && pScanLogicNode->pScanCols != NULL) {
2943
0
      SNode* pTarget = NULL;
2944
0
      FOREACH(pTarget, pScanLogicNode->node.pTargets) {
2945
        // Unwrap STargetNode wrapper if present
2946
0
        const SNode* pTgtInner = pTarget;
2947
0
        if (nodeType(pTgtInner) == QUERY_NODE_TARGET) {
2948
0
          pTgtInner = ((const STargetNode*)pTgtInner)->pExpr;
2949
0
        }
2950
0
        if (NULL == pTgtInner || nodeType(pTgtInner) != QUERY_NODE_COLUMN) {
2951
0
          reorderOk = false;
2952
0
          break;
2953
0
        }
2954
0
        const SColumnNode* pTgtCol = (const SColumnNode*)pTgtInner;
2955
0
        bool               found   = false;
2956
0
        SNode*             pScanCol = NULL;
2957
0
        FOREACH(pScanCol, pScanLogicNode->pScanCols) {
2958
0
          SColumnNode* pSCol = (SColumnNode*)pScanCol;
2959
0
          if ((pSCol->colId != 0 && pSCol->colId == pTgtCol->colId) ||
2960
0
              (pSCol->colId == 0 && 0 == strcmp(pSCol->colName, pTgtCol->colName))) {
2961
0
            SNode* pClone = NULL;
2962
0
            PLAN_ERR_JRET(nodesCloneNode(pScanCol, &pClone));
2963
0
            PLAN_ERR_JRET(nodesListMakeStrictAppend(&pReordered, pClone));
2964
0
            found = true;
2965
0
            break;
2966
0
          }
2967
0
        }
2968
0
        if (!found || !reorderOk) { reorderOk = false; break; }
2969
0
      }
2970
0
      if (!reorderOk || pReordered == NULL ||
2971
0
          LIST_LENGTH(pReordered) != LIST_LENGTH(pScanLogicNode->node.pTargets)) {
2972
0
        NODES_DESTORY_LIST(pReordered);
2973
0
      } else {
2974
        // Reorder succeeded. Append any extra pScanCols columns that are NOT in
2975
        // pTargets (e.g. primary-key columns added for WINDOW JOIN addPrimEqCond
2976
        // slot resolution that are absent from the SELECT list).  These must be
2977
        // present in the slot hash so that createMergeJoinPhysiNode can resolve
2978
        // addPrimEqCond column slot IDs without PLAN_SLOT_NOT_FOUND.
2979
0
        SNode* pSc = NULL;
2980
0
        FOREACH(pSc, pScanLogicNode->pScanCols) {
2981
0
          if (QUERY_NODE_COLUMN != nodeType(pSc)) continue;
2982
0
          SColumnNode* pSCol = (SColumnNode*)pSc;
2983
0
          bool alreadyIn = false;
2984
0
          SNode* pEx = NULL;
2985
0
          FOREACH(pEx, pReordered) {
2986
0
            if (QUERY_NODE_COLUMN != nodeType(pEx)) continue;
2987
0
            SColumnNode* pExCol = (SColumnNode*)pEx;
2988
0
            if ((pExCol->colId != 0 && pExCol->colId == pSCol->colId) ||
2989
0
                (pExCol->colId == 0 && strcmp(pExCol->colName, pSCol->colName) == 0 &&
2990
0
                 strcmp(pExCol->tableAlias, pSCol->tableAlias) == 0)) {
2991
0
              alreadyIn = true;
2992
0
              break;
2993
0
            }
2994
0
          }
2995
0
          if (!alreadyIn) {
2996
0
            SNode* pClone = NULL;
2997
0
            PLAN_ERR_JRET(nodesCloneNode(pSc, &pClone));
2998
0
            PLAN_ERR_JRET(nodesListMakeStrictAppend(&pReordered, pClone));
2999
0
          }
3000
0
        }
3001
0
      }
3002
0
    }
3003
3004
0
    if (pReordered != NULL) {
3005
0
      pScan->pScanCols = pReordered;
3006
0
      pReordered = NULL;
3007
0
    } else {
3008
      // Fallback: clone in original pScanCols order
3009
0
      PLAN_ERR_JRET(nodesCloneList(pScanLogicNode->pScanCols, &pScan->pScanCols));
3010
0
    }
3011
0
  }
3012
3013
  // Build pColTypeMappings from the same single source used by remote SQL
3014
  // rendering: the top-level remote plan's pTargets.  Only when there is no
3015
  // remote plan at all do we fall back to the local scan columns.  If a remote
3016
  // plan exists but its top-level outputs are still empty, that is a real plan
3017
  // inconsistency and must fail loudly; otherwise the descriptor, pTargets and
3018
  // remote SQL output order will diverge.
3019
0
  {
3020
0
    SNodeList* pOutputCols = pScan->pScanCols;  // default: no pushed plan
3021
0
    const char* pOutputColsSource = "pScanCols";
3022
0
    if (useRemoteResultSchema) {
3023
0
      PLAN_ERR_JRET(
3024
0
          fqAlignRemotePassthroughOutputs(pScanLogicNode->pRemoteLogicPlan, pScan->pScanCols));
3025
0
      PLAN_ERR_JRET(fqRefreshRemoteOutputTargets(pScanLogicNode->pRemoteLogicPlan));
3026
3027
0
      SNodeList* pRemoteTargets = fqGetRemoteOutputTargets(pScanLogicNode->pRemoteLogicPlan);
3028
0
      planDebug("FQ-ColMappings: inspect top remote targets, remoteRootType=%d targets=%p len=%d",
3029
0
                (int)nodeType((SNode*)pScanLogicNode->pRemoteLogicPlan), pRemoteTargets,
3030
0
                pRemoteTargets ? LIST_LENGTH(pRemoteTargets) : 0);
3031
0
      if (pRemoteTargets != NULL && LIST_LENGTH(pRemoteTargets) > 0) {
3032
0
        pOutputCols = pRemoteTargets;
3033
0
        pOutputColsSource = "pRemotePlan->pTargets";
3034
0
      } else {
3035
0
        planError("FQ-ColMappings: remote plan exists but top remote targets are empty, rootType=%d scanCols=%d",
3036
0
                  (int)nodeType((SNode*)pScanLogicNode->pRemoteLogicPlan),
3037
0
                  pScan->pScanCols ? LIST_LENGTH(pScan->pScanCols) : 0);
3038
0
        PLAN_ERR_JRET(TSDB_CODE_PLAN_INVALID_FEDERATED_SCAN);
3039
0
      }
3040
0
    }
3041
3042
0
    int32_t numCols = pOutputCols ? LIST_LENGTH(pOutputCols) : 0;
3043
0
    int32_t outputIdx = 0;
3044
0
    SNode* pExprNode = NULL;
3045
0
    FOREACH(pExprNode, pOutputCols) {
3046
0
      SNode* pExpr = fqResolveRemoteMappingExpr(pExprNode);
3047
0
      if (NULL == pExpr) {
3048
0
        planError("FQ-ColMappings: outputIdx=%d has NULL mapping expr, source=%s exprNodeType=%d",
3049
0
                  outputIdx, pOutputColsSource, (int)nodeType(pExprNode));
3050
0
        PLAN_ERR_JRET(TSDB_CODE_PLAN_INVALID_FEDERATED_SCAN);
3051
0
      }
3052
0
      planDebug("FQ-ColMappings: count outputIdx=%d exprNodeType=%d exprType=%d includeInMappings=1",
3053
0
                outputIdx, (int)nodeType(pExprNode), (int)nodeType(pExpr));
3054
0
      ++outputIdx;
3055
0
    }
3056
3057
0
    planDebug("FQ-ColMappings: source=%s outputLen=%d mappingCols=%d",
3058
0
              pOutputColsSource, pOutputCols ? LIST_LENGTH(pOutputCols) : 0, numCols);
3059
0
    if (numCols > 0) {
3060
0
      pScan->pColTypeMappings =
3061
0
          (SExtColTypeMapping*)taosMemoryCalloc(numCols, sizeof(SExtColTypeMapping));
3062
0
      if (NULL == pScan->pColTypeMappings) {
3063
0
        PLAN_ERR_JRET(TSDB_CODE_OUT_OF_MEMORY);
3064
0
      }
3065
0
      pScan->numColTypeMappings = numCols;
3066
3067
0
      int32_t colIdx = 0;
3068
0
      outputIdx = 0;
3069
0
      FOREACH(pExprNode, pOutputCols) {
3070
0
        SNode* pExpr = fqResolveRemoteMappingExpr(pExprNode);
3071
0
        if (NULL == pExpr) {
3072
0
          planError("FQ-ColMappings: outputIdx=%d resolved NULL expr during fill, source=%s exprNodeType=%d",
3073
0
                    outputIdx, pOutputColsSource, (int)nodeType(pExprNode));
3074
0
          PLAN_ERR_JRET(TSDB_CODE_PLAN_INVALID_FEDERATED_SCAN);
3075
0
        }
3076
3077
0
        planDebug("FQ-ColMappings[%d]: outputIdx=%d exprNodeType=%d exprType=%d type=%d precision=%d scale=%d bytes=%d",
3078
0
                  colIdx, outputIdx, (int)nodeType(pExprNode), (int)nodeType(pExpr),
3079
0
                  (int)((SExprNode*)pExpr)->resType.type, (int)((SExprNode*)pExpr)->resType.precision,
3080
0
                  (int)((SExprNode*)pExpr)->resType.scale, (int)((SExprNode*)pExpr)->resType.bytes);
3081
0
        pScan->pColTypeMappings[colIdx].tdType = ((SExprNode*)pExpr)->resType;
3082
0
        const char* pOutputName = fqResolveRemoteOutputName(pExprNode);
3083
0
        if (NULL != pOutputName && '\0' != pOutputName[0]) {
3084
0
          tstrncpy(pScan->pColTypeMappings[colIdx].colName, pOutputName,
3085
0
                   sizeof(pScan->pColTypeMappings[colIdx].colName));
3086
0
        }
3087
0
        planDebug("FQ-ColMappings[%d]: outputIdx=%d outputName=%s source=%s",
3088
0
                  colIdx, outputIdx,
3089
0
                  pScan->pColTypeMappings[colIdx].colName[0] != '\0' ? pScan->pColTypeMappings[colIdx].colName : "(empty)",
3090
0
                  pOutputColsSource);
3091
0
        ++colIdx;
3092
0
        ++outputIdx;
3093
0
      }
3094
3095
0
      if (colIdx != numCols) {
3096
0
        planError("FQ-ColMappings: filled mapping count mismatch, source=%s expected=%d actual=%d",
3097
0
                  pOutputColsSource, numCols, colIdx);
3098
0
        PLAN_ERR_JRET(TSDB_CODE_PLAN_INVALID_FEDERATED_SCAN);
3099
0
      }
3100
0
    }
3101
0
  }
3102
3103
0
  PLAN_ERR_JRET(fqBuildTimestampTypeHints(pScan, pExtNode));
3104
  // Propagate two-pass mode only when the repeat-scan aggregate still runs locally.
3105
  // If the remote subtree already contains the repeat-scan AGG, FederatedScan must
3106
  // return the final remote rows once, not PRE_SCAN + MAIN_SCAN duplicates.
3107
0
  bool requestTwoPass = (pScanLogicNode->scanSeq[0] > 1);
3108
0
  bool remoteOwnsRepeatScan =
3109
0
      useRemoteLogicPlan && fqRemotePlanHasRepeatScanAgg((const SLogicNode*)pScanLogicNode->pRemoteLogicPlan);
3110
0
  pScan->twoPassMode = requestTwoPass && !remoteOwnsRepeatScan;
3111
0
  planDebug("FQ-PhysiFedScan: source=%s scanSeq0=%d requestTwoPass=%d remoteOwnsRepeatScan=%d finalTwoPass=%d"
3112
0
            " useRemoteLogicPlan=%d remoteRootType=%d",
3113
0
            pExtNode->sourceName, (int)pScanLogicNode->scanSeq[0], (int)requestTwoPass,
3114
0
            (int)remoteOwnsRepeatScan, (int)pScan->twoPassMode, (int)useRemoteLogicPlan,
3115
0
            pScanLogicNode->pRemoteLogicPlan ? (int)nodeType((SNode*)pScanLogicNode->pRemoteLogicPlan) : -1);
3116
  // Copy client timezone name so the Executor can reconstruct the timezone_t via tzalloc().
3117
  // Used by the Connector to convert timezone-naive source timestamps (MySQL DATETIME,
3118
  // PG timestamp without time zone, etc.) using the correct client timezone.
3119
0
  fqSetFederatedScanTimezone(pCxt, pScan);
3120
3121
  // Add output data block slots.
3122
  // When pRemoteLogicPlan exists, FedScan executes the remote plan and returns its
3123
  // top-level output.  The output descriptor must reflect what the remote plan actually
3124
  // produces, not the original scan columns.  Only when pRemoteLogicPlan is NULL (pure
3125
  // scan, no pushdown) do we fall back to pScanCols.  A non-NULL remote plan with an
3126
  // empty output list is a real inconsistency and must fail loudly.
3127
0
  SNodeList* pSlotCols = pScan->pScanCols;  // default: no remote plan
3128
0
  {
3129
0
    if (useRemoteResultSchema) {
3130
0
      PLAN_ERR_JRET(fqRefreshRemoteOutputTargets(pScanLogicNode->pRemoteLogicPlan));
3131
3132
0
      SNodeList* pRemoteTargets = fqGetRemoteOutputTargets(pScanLogicNode->pRemoteLogicPlan);
3133
0
      planDebug("FQ-Slots: inspect top remote targets for FedScan output descriptor, type=%d len=%d",
3134
0
                (int)nodeType((SNode*)pScanLogicNode->pRemoteLogicPlan),
3135
0
                pRemoteTargets ? LIST_LENGTH(pRemoteTargets) : 0);
3136
0
      if (pRemoteTargets != NULL && LIST_LENGTH(pRemoteTargets) > 0) {
3137
0
        pSlotCols = pRemoteTargets;
3138
0
      } else {
3139
0
        planError("FQ-Slots: remote plan exists but top remote targets are empty, rootType=%d scanCols=%d",
3140
0
                  (int)nodeType((SNode*)pScanLogicNode->pRemoteLogicPlan),
3141
0
                  pScan->pScanCols ? LIST_LENGTH(pScan->pScanCols) : 0);
3142
0
        PLAN_ERR_JRET(TSDB_CODE_PLAN_INVALID_FEDERATED_SCAN);
3143
0
      }
3144
0
    }
3145
    // When pRemoteLogicPlan is non-NULL, the FedScan output descriptor must be
3146
    // rebuilt from scratch in remote-result order, then local-only dependency
3147
    // columns are appended as reserve slots. Reusing makePhysiNode's initial
3148
    // descriptor leaks the local scan-target order into output slots and can
3149
    // swap visible/hidden columns for partial-project pushdown.
3150
0
    if (useRemoteLogicPlan && pSlotCols != pScan->pScanCols && pScan->pScanCols != NULL) {
3151
0
      PLAN_ERR_JRET(nodesCloneList(pScan->pScanCols, &pReserveSlotCols));
3152
0
      planDebug("FQ-Slots: rebuilt FedScan descriptor from remote targets (len=%d) plus reserve scanCols (len=%d)",
3153
0
                (int)LIST_LENGTH(pSlotCols), (int)LIST_LENGTH(pReserveSlotCols));
3154
0
      PLAN_ERR_JRET(
3155
0
          fqRebuildDataBlockDesc(pCxt, pScan->node.pOutputDataBlockDesc, pSlotCols, pReserveSlotCols));
3156
0
    } else {
3157
0
      PLAN_ERR_JRET(addDataBlockSlots(pCxt, pSlotCols, pScan->node.pOutputDataBlockDesc));
3158
0
    }
3159
0
  }
3160
3161
  // Add pseudo-column slots (e.g. TBNAME for PARTITION BY TBNAME on InfluxDB).
3162
  // Normal table scans handle this via pScanPseudoCols in createSimpleScanPhysiNode.
3163
  // Without this, PARTITION BY TBNAME on a federated scan crashes: the partition
3164
  // operator's group-key column references a slot that doesn't exist in the data
3165
  // block, causing a type mismatch and buffer overflow in doHashPartition.
3166
0
  if (NULL != pScanLogicNode->pScanPseudoCols) {
3167
0
    PLAN_ERR_JRET(
3168
0
        addDataBlockSlots(pCxt, pScanLogicNode->pScanPseudoCols, pScan->node.pOutputDataBlockDesc));
3169
0
  }
3170
3171
0
  if (pReserveSlotCols != NULL) {
3172
0
    nodesDestroyList(pReserveSlotCols);
3173
0
    pReserveSlotCols = NULL;
3174
0
  }
3175
  // Set correct slot IDs in the cloned conditions using setNodeSlotId.
3176
  // Only columns that are present in the output/reserve slot descriptor can be
3177
  // resolved here; conditions pushed to the remote side are handled remotely.
3178
0
  if (pScanLogicNode->node.pConditions != NULL) {
3179
0
    PLAN_ERR_JRET(setNodeSlotId(pCxt, pScan->node.pOutputDataBlockDesc->dataBlockId, -1,
3180
0
                                pScanLogicNode->node.pConditions, &pScan->node.pConditions));
3181
0
    planDebug("FQ-PHY FedScan pConditions: setNodeSlotId code=0x%x pConditions=%p",
3182
0
              code, pScan->node.pConditions);
3183
0
  } else {
3184
0
    planDebug("FQ-PHY FedScan pConditions: logic pConditions is NULL (already pushed or none)");
3185
0
  }
3186
3187
  // Force MERGE subplan type to avoid DATA_SRC_EP_MISS (external has no vnode)
3188
0
  pSubplan->subplanType = SUBPLAN_TYPE_MERGE;
3189
0
  pSubplan->msgType     = TDMT_SCH_MERGE_QUERY;
3190
3191
  // * Mark the physical plan as containing a federated scan
3192
0
  pCxt->hasFederatedScan = true;
3193
3194
  // -- Build the remote plan as a LOGIC tree (pure clone - no physi conversion) --
3195
  //         .pScanCols    -> flat leaf SELECT list (must already be non-empty)
3196
  //         .pConditions  -> WHERE clause
3197
  //         .node.pLimit  -> LIMIT / OFFSET
3198
3199
  // pRemotePlan is a logic-node subtree: nodesRemotePlanToSQL renders it directly
3200
  // (it reads only column names / expressions / functions - never physi slot ids),
3201
  // and TLV ships it to the server which re-renders.  Two cases:
3202
  //   (a) pRemoteLogicPlan set (FqPushdown collected a chain): clone it verbatim -
3203
  //       it already carries its own logic SCAN leaf (cloned by fqCommitPushChain).
3204
  //   (b) pRemoteLogicPlan NULL: build a bare logic SCAN leaf carrying the remote
3205
  //       FROM/SELECT/WHERE/LIMIT so the connector runs a flat "SELECT ... FROM ...".
3206
0
  planDebug("FQ-PHY: pScanLogicNode=%p pRemoteLogicPlan=%p type=%d useRemoteLogicPlan=%d", pScanLogicNode,
3207
0
            pScanLogicNode->pRemoteLogicPlan,
3208
0
            pScanLogicNode->pRemoteLogicPlan ? (int)nodeType(pScanLogicNode->pRemoteLogicPlan) : -1,
3209
0
            (int)useRemoteLogicPlan);
3210
0
  if (useRemoteLogicPlan) {
3211
0
    PLAN_ERR_JRET(nodesCloneNode(pScanLogicNode->pRemoteLogicPlan, &pRemoteRoot));
3212
0
    planDebug("FQ-PHY: after clone pRemoteRoot=%p type=%d code=0x%x",
3213
0
              pRemoteRoot, pRemoteRoot ? (int)nodeType(pRemoteRoot) : -1, code);
3214
0
    if (pRemoteRoot != NULL) {
3215
0
      SLogicNode* pRemoteLogicRoot = (SLogicNode*)pRemoteRoot;
3216
0
      planDebug("FQ-PHY: cloned remote root detail type=%d nChildren=%d targets=%d limit=%p",
3217
0
                (int)nodeType(pRemoteRoot),
3218
0
                pRemoteLogicRoot->pChildren ? LIST_LENGTH(pRemoteLogicRoot->pChildren) : 0,
3219
0
                pRemoteLogicRoot->pTargets ? LIST_LENGTH(pRemoteLogicRoot->pTargets) : 0,
3220
0
                pRemoteLogicRoot->pLimit);
3221
0
      SScanLogicNode* pRemoteLeaf = fqFindExternalLogicLeafScan((SLogicNode*)pRemoteRoot);
3222
0
      planDebug("FQ-PHY: cloned remote leaf detail leaf=%p hasCond=%d scanCols=%d targets=%d remotePlan=%p source=%s table=%s",
3223
0
                pRemoteLeaf, (NULL != pRemoteLeaf && NULL != pRemoteLeaf->node.pConditions),
3224
0
                (NULL != pRemoteLeaf && NULL != pRemoteLeaf->pScanCols) ? LIST_LENGTH(pRemoteLeaf->pScanCols) : 0,
3225
0
                (NULL != pRemoteLeaf && NULL != pRemoteLeaf->node.pTargets) ? LIST_LENGTH(pRemoteLeaf->node.pTargets) : 0,
3226
0
                NULL != pRemoteLeaf ? pRemoteLeaf->pRemoteLogicPlan : NULL,
3227
0
                (NULL != pRemoteLeaf && NULL != pRemoteLeaf->pExtTableNode)
3228
0
                    ? ((SExtTableNode*)pRemoteLeaf->pExtTableNode)->sourceName
3229
0
                    : "(null)",
3230
0
                (NULL != pRemoteLeaf && NULL != pRemoteLeaf->pExtTableNode)
3231
0
                    ? ((SExtTableNode*)pRemoteLeaf->pExtTableNode)->table.tableName
3232
0
                    : "(null)");
3233
0
    }
3234
0
    PLAN_ERR_JRET(fqAlignRemotePassthroughOutputs(pRemoteRoot, pScan->pScanCols));
3235
0
    if (pRemoteRoot != NULL) {
3236
0
      SLogicNode* pRemoteLogicRoot = (SLogicNode*)pRemoteRoot;
3237
0
      planDebug("FQ-PHY: aligned remote root detail type=%d nChildren=%d targets=%d limit=%p",
3238
0
                (int)nodeType(pRemoteRoot),
3239
0
                pRemoteLogicRoot->pChildren ? LIST_LENGTH(pRemoteLogicRoot->pChildren) : 0,
3240
0
                pRemoteLogicRoot->pTargets ? LIST_LENGTH(pRemoteLogicRoot->pTargets) : 0,
3241
0
                pRemoteLogicRoot->pLimit);
3242
0
    }
3243
0
    PLAN_ERR_JRET(fqRefreshRemoteOutputTargets(pRemoteRoot));
3244
0
    if (pRemoteRoot != NULL) {
3245
0
      SLogicNode* pRemoteLogicRoot = (SLogicNode*)pRemoteRoot;
3246
0
      planDebug("FQ-PHY: refreshed remote output targets detail type=%d nChildren=%d targets=%d limit=%p",
3247
0
                (int)nodeType(pRemoteRoot),
3248
0
                pRemoteLogicRoot->pChildren ? LIST_LENGTH(pRemoteLogicRoot->pChildren) : 0,
3249
0
                pRemoteLogicRoot->pTargets ? LIST_LENGTH(pRemoteLogicRoot->pTargets) : 0,
3250
0
                pRemoteLogicRoot->pLimit);
3251
0
      SScanLogicNode* pRemoteLeaf = fqFindExternalLogicLeafScan((SLogicNode*)pRemoteRoot);
3252
0
      planDebug("FQ-PHY: refreshed remote leaf detail leaf=%p hasCond=%d scanCols=%d targets=%d remotePlan=%p source=%s table=%s",
3253
0
                pRemoteLeaf, (NULL != pRemoteLeaf && NULL != pRemoteLeaf->node.pConditions),
3254
0
                (NULL != pRemoteLeaf && NULL != pRemoteLeaf->pScanCols) ? LIST_LENGTH(pRemoteLeaf->pScanCols) : 0,
3255
0
                (NULL != pRemoteLeaf && NULL != pRemoteLeaf->node.pTargets) ? LIST_LENGTH(pRemoteLeaf->node.pTargets) : 0,
3256
0
                NULL != pRemoteLeaf ? pRemoteLeaf->pRemoteLogicPlan : NULL,
3257
0
                (NULL != pRemoteLeaf && NULL != pRemoteLeaf->pExtTableNode)
3258
0
                    ? ((SExtTableNode*)pRemoteLeaf->pExtTableNode)->sourceName
3259
0
                    : "(null)",
3260
0
                (NULL != pRemoteLeaf && NULL != pRemoteLeaf->pExtTableNode)
3261
0
                    ? ((SExtTableNode*)pRemoteLeaf->pExtTableNode)->table.tableName
3262
0
                    : "(null)");
3263
0
    }
3264
0
  } else {
3265
0
    PLAN_ERR_JRET(nodesMakeNode(QUERY_NODE_LOGIC_PLAN_SCAN, &pRemoteRoot));
3266
0
    SScanLogicNode* pLeaf = (SScanLogicNode*)pRemoteRoot;
3267
0
    pLeaf->scanType = SCAN_TYPE_EXTERNAL;
3268
    // FROM: clone the external table node (table identity for SQL generation).
3269
0
    PLAN_ERR_JRET(nodesCloneNode(pScanLogicNode->pExtTableNode, &pLeaf->pExtTableNode));
3270
    // SELECT: clone the de-duplicated column set the optimizer collected.
3271
    // Use pScan->pScanCols (already reordered to pTargets order above), NOT the
3272
    // raw pScanLogicNode->pScanCols.  The remote SQL SELECT list rendered from
3273
    // this leaf MUST match the column order of pColTypeMappings (built from
3274
    // pScan->pScanCols below); otherwise the connector decodes result column N
3275
    // with mapping[N] of a different column -> 0x6418 (e.g. "unexpected OID 701
3276
    // for TIMESTAMP column 0").  nodesCollectColumns visits WHERE-subquery /
3277
    // ORDER BY columns before the projection, so the raw order diverges from
3278
    // pTargets only when a WHERE subquery (REMOTE_*) is present - exactly the
3279
    // failing case.
3280
0
    PLAN_ERR_JRET(nodesCloneList(pScan->pScanCols, &pLeaf->pScanCols));
3281
    // Optional query conditions stay local in this fallback. Saved binding filters
3282
    // still belong to the remote leaf, even when no optimizer rule runs.
3283
0
    code = nodesCloneNode(pScanLogicNode->pPushedConditions, &pLeaf->pPushedConditions);
3284
0
    QUERY_CHECK_CODE(code, lino, _return);
3285
    // LIMIT: makePhysiNode moved pScanLogicNode->pLimit to pScan->node.pLimit.
3286
0
    if (pScan->node.pLimit != NULL) {
3287
0
      PLAN_ERR_JRET(nodesCloneNode(pScan->node.pLimit, &pLeaf->node.pLimit));
3288
0
    }
3289
0
    planDebug("FQ-PHY: built fallback bare remote scan leaf=%p scanCols=%d targets=%d hasCond=%d hasLimit=%d",
3290
0
              pLeaf, pLeaf->pScanCols ? LIST_LENGTH(pLeaf->pScanCols) : 0,
3291
0
              pLeaf->node.pTargets ? LIST_LENGTH(pLeaf->node.pTargets) : 0,
3292
0
              NULL != pLeaf->node.pConditions, NULL != pLeaf->node.pLimit);
3293
0
  }
3294
3295
0
  if (pRemoteRoot != NULL) {
3296
0
    code = fqAttachRequiredScanConditions((SLogicNode*)pRemoteRoot, &pScan->pushdownFlags);
3297
0
    QUERY_CHECK_CODE(code, lino, _return);
3298
0
    PLAN_ERR_JRET(fqRefreshRemoteOutputTargets(pRemoteRoot));
3299
3300
0
    SLogicNode* pRemoteLogicRoot = (SLogicNode*)pRemoteRoot;
3301
0
    planDebug("FQ-PHY: finalized remote root detail type=%d nChildren=%d targets=%d hasCond=%d limit=%p",
3302
0
              (int)nodeType(pRemoteRoot),
3303
0
              pRemoteLogicRoot->pChildren ? LIST_LENGTH(pRemoteLogicRoot->pChildren) : 0,
3304
0
              pRemoteLogicRoot->pTargets ? LIST_LENGTH(pRemoteLogicRoot->pTargets) : 0,
3305
0
              NULL != pRemoteLogicRoot->pConditions, pRemoteLogicRoot->pLimit);
3306
0
    SScanLogicNode* pRemoteLeaf = fqFindExternalLogicLeafScan((SLogicNode*)pRemoteRoot);
3307
0
    planDebug("FQ-PHY: finalized remote leaf detail leaf=%p hasCond=%d scanCols=%d targets=%d remotePlan=%p source=%s table=%s",
3308
0
              pRemoteLeaf, (NULL != pRemoteLeaf && NULL != pRemoteLeaf->node.pConditions),
3309
0
              (NULL != pRemoteLeaf && NULL != pRemoteLeaf->pScanCols) ? LIST_LENGTH(pRemoteLeaf->pScanCols) : 0,
3310
0
              (NULL != pRemoteLeaf && NULL != pRemoteLeaf->node.pTargets) ? LIST_LENGTH(pRemoteLeaf->node.pTargets) : 0,
3311
0
              NULL != pRemoteLeaf ? pRemoteLeaf->pRemoteLogicPlan : NULL,
3312
0
              (NULL != pRemoteLeaf && NULL != pRemoteLeaf->pExtTableNode)
3313
0
                  ? ((SExtTableNode*)pRemoteLeaf->pExtTableNode)->sourceName
3314
0
                  : "(null)",
3315
0
              (NULL != pRemoteLeaf && NULL != pRemoteLeaf->pExtTableNode)
3316
0
                  ? ((SExtTableNode*)pRemoteLeaf->pExtTableNode)->table.tableName
3317
0
                  : "(null)");
3318
0
  }
3319
3320
  // Attach the inner pRemotePlan (logic) tree to the outer Mode-1 wrapper.
3321
0
  pScan->pRemotePlan = pRemoteRoot;
3322
0
  pRemoteRoot = NULL;
3323
3324
0
  planDebug("FQ-PHY: FedScan=%p pRemotePlan=%p type=%d",
3325
0
            pScan, pScan->pRemotePlan,
3326
0
            pScan->pRemotePlan ? (int)nodeType(pScan->pRemotePlan) : -1);
3327
0
  *pPhyNode = (SPhysiNode*)pScan;
3328
0
  pScan = NULL;
3329
3330
0
_return:
3331
0
  nodesDestroyList(pReordered);
3332
0
  nodesDestroyList(pReserveSlotCols);
3333
0
  nodesDestroyNode(pRemoteRoot);
3334
0
  nodesDestroyNode((SNode*)pScan);
3335
0
  if (TSDB_CODE_SUCCESS != code) {
3336
0
    planError("%s failed since %s", __func__, tstrerror(code));
3337
0
  }
3338
0
  return code;
3339
0
}
3340
3341
static int32_t createScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan, SScanLogicNode* pScanLogicNode,
3342
0
                                   SPhysiNode** pPhyNode) {
3343
3344
0
  switch (pScanLogicNode->scanType) {
3345
0
    case SCAN_TYPE_TAG:
3346
0
      PLAN_ERR_RET(createTagScanPhysiNode(pCxt, pSubplan, pScanLogicNode, pPhyNode));
3347
0
      break;
3348
0
    case SCAN_TYPE_BLOCK_INFO:
3349
0
      PLAN_ERR_RET(createSimpleScanPhysiNode(pCxt, pSubplan, pScanLogicNode, pPhyNode));
3350
0
      break;
3351
0
    case SCAN_TYPE_TABLE_COUNT:
3352
0
      PLAN_ERR_RET(createTableCountScanPhysiNode(pCxt, pSubplan, pScanLogicNode, pPhyNode));
3353
0
      break;
3354
0
    case SCAN_TYPE_LAST_ROW:
3355
0
      PLAN_ERR_RET(createLastRowScanPhysiNode(pCxt, pSubplan, pScanLogicNode, pPhyNode));
3356
0
      break;
3357
0
    case SCAN_TYPE_TABLE:
3358
0
      PLAN_ERR_RET(createTableScanPhysiNode(pCxt, pSubplan, pScanLogicNode, pPhyNode));
3359
0
      break;
3360
0
    case SCAN_TYPE_SYSTEM_TABLE:
3361
0
      PLAN_ERR_RET(createSystemTableScanPhysiNode(pCxt, pSubplan, pScanLogicNode, pPhyNode));
3362
0
      break;
3363
0
    case SCAN_TYPE_STREAM:
3364
0
      PLAN_ERR_RET(createStreamScanPhysiNode(pCxt, pSubplan, pScanLogicNode, pPhyNode));
3365
0
      break;
3366
0
    case SCAN_TYPE_TABLE_MERGE:
3367
0
      PLAN_ERR_RET(createTableMergeScanPhysiNode(pCxt, pSubplan, pScanLogicNode, pPhyNode));
3368
0
      break;
3369
0
    case SCAN_TYPE_EXTERNAL:
3370
0
      PLAN_ERR_RET(createFederatedScanPhysiNode(pCxt, pSubplan, pScanLogicNode, pPhyNode));
3371
0
      break;
3372
0
    default:
3373
0
      PLAN_ERR_RET(generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PLAN_INTERNAL_ERROR,
3374
0
                                       "invalid scan type:%d", pScanLogicNode->scanType));
3375
0
      break;
3376
0
  }
3377
3378
0
  if (inStreamTriggerClause(pCxt->pPlanCxt) && !pCxt->pPlanCxt->streamCxt.triggerScanSubplan) {
3379
0
    pCxt->pPlanCxt->streamCxt.triggerScanSubplan = (SNode*)pSubplan;
3380
0
  }
3381
0
  if (inStreamCalcClause(pCxt->pPlanCxt)) {
3382
0
    SStreamCalcScan pStreamCalcScan = {0};
3383
0
    pStreamCalcScan.vgList = taosArrayInit(1, sizeof(int32_t));
3384
    // External-source scans have no vgroup; execNode.nodeId is 0 (unset).
3385
    // Use SNODE_HANDLE as a sentinel so mnode can distinguish ext-source calc
3386
    // readers from normal vnode readers and assign the real snodeId / epset.
3387
0
    int32_t calcNodeId = (pScanLogicNode->scanType == SCAN_TYPE_EXTERNAL)
3388
0
                             ? SNODE_HANDLE
3389
0
                             : pSubplan->execNode.nodeId;
3390
0
    if (NULL == taosArrayPush(pStreamCalcScan.vgList, &calcNodeId)) {
3391
0
      PLAN_ERR_RET(terrno);
3392
0
    }
3393
0
    pStreamCalcScan.scanPlan = (void*)pSubplan;
3394
    // Federated calc: record this scan's external source/table identity so the
3395
    // mnode can bind each calc reader to the correct per-source spec when the
3396
    // calc query JOINs multiple external tables/sources. tsColumn is resolved
3397
    // here (from pExtMeta) because pExtMeta is runtime-only and not serialized.
3398
0
    if (pScanLogicNode->scanType == SCAN_TYPE_EXTERNAL && pScanLogicNode->pExtTableNode != NULL) {
3399
0
      SExtTableNode* pExt = (SExtTableNode*)pScanLogicNode->pExtTableNode;
3400
0
      tstrncpy(pStreamCalcScan.sourceName, pExt->sourceName, sizeof(pStreamCalcScan.sourceName));
3401
0
      const char* remoteTbl =
3402
0
          (pExt->remoteTableName[0] != '\0') ? pExt->remoteTableName : pExt->table.tableName;
3403
0
      tstrncpy(pStreamCalcScan.extTable, remoteTbl, sizeof(pStreamCalcScan.extTable));
3404
0
      if (pExt->tsPrimaryColIdx >= 0 && pExt->pExtMeta != NULL &&
3405
0
          pExt->tsPrimaryColIdx < pExt->pExtMeta->numOfCols && pExt->pExtMeta->pCols != NULL) {
3406
0
        tstrncpy(pStreamCalcScan.tsColumn, pExt->pExtMeta->pCols[pExt->tsPrimaryColIdx].colName,
3407
0
                 sizeof(pStreamCalcScan.tsColumn));
3408
0
      }
3409
0
    }
3410
0
    if (pScanLogicNode->placeholderType == SP_PARTITION_ROWS) {
3411
0
      pStreamCalcScan.readFromCache = true;
3412
0
    }
3413
0
    if (NULL == taosArrayPush(pCxt->pPlanCxt->streamCxt.calcScanPlanArray, &pStreamCalcScan)) {
3414
0
      PLAN_ERR_RET(terrno);
3415
0
    }
3416
0
  }
3417
3418
0
  return TSDB_CODE_SUCCESS;
3419
0
}
3420
3421
0
static int32_t getJoinDataBlockDescNode(SNodeList* pChildren, int32_t idx, SDataBlockDescNode** ppDesc) {
3422
0
  if (2 == pChildren->length) {
3423
0
    *ppDesc = ((SPhysiNode*)nodesListGetNode(pChildren, idx))->pOutputDataBlockDesc;
3424
0
  } else if (1 == pChildren->length &&
3425
0
             nodeType(nodesListGetNode(pChildren, 0)) == QUERY_NODE_PHYSICAL_PLAN_GROUP_CACHE) {
3426
0
    SGroupCachePhysiNode* pGrpCache = (SGroupCachePhysiNode*)nodesListGetNode(pChildren, 0);
3427
0
    *ppDesc = ((SPhysiNode*)nodesListGetNode(pGrpCache->node.pChildren, idx))->pOutputDataBlockDesc;
3428
0
  } else {
3429
0
    planError("Invalid join children num:%d or child type:%d", pChildren->length,
3430
0
              nodeType(nodesListGetNode(pChildren, 0)));
3431
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
3432
0
  }
3433
3434
0
  return TSDB_CODE_SUCCESS;
3435
0
}
3436
3437
static int32_t setColEqList(SNode* pEqCond, int64_t leftBlkId, int64_t rightBlkId, SNodeList** ppLeft,
3438
0
                            SNodeList** ppRight) {
3439
0
  int32_t code = 0;
3440
0
  if (QUERY_NODE_OPERATOR == nodeType(pEqCond) && ((SOperatorNode*)pEqCond)->opType == OP_TYPE_EQUAL) {
3441
0
    SOperatorNode* pOp = (SOperatorNode*)pEqCond;
3442
0
    SNode*         pNew = NULL;
3443
0
    if (QUERY_NODE_COLUMN != nodeType(pOp->pLeft) || QUERY_NODE_COLUMN != nodeType(pOp->pRight)) {
3444
0
      planError("invalid col equal list node type, leftType:%d, rightType:%d", nodeType(pOp->pLeft), nodeType(pOp->pRight));
3445
0
      return TSDB_CODE_PLAN_INTERNAL_ERROR;
3446
0
    }
3447
0
    if (leftBlkId == ((SColumnNode*)pOp->pLeft)->dataBlockId) {
3448
0
      code = nodesCloneNode(pOp->pLeft, &pNew);
3449
0
      if (TSDB_CODE_SUCCESS == code) {
3450
0
        code = nodesListMakeStrictAppend(ppLeft, pNew);
3451
0
      }
3452
0
    } else if (rightBlkId == ((SColumnNode*)pOp->pLeft)->dataBlockId) {
3453
0
      code = nodesCloneNode(pOp->pLeft, &pNew);
3454
0
      if (TSDB_CODE_SUCCESS == code) {
3455
0
        code = nodesListMakeStrictAppend(ppRight, pNew);
3456
0
      }
3457
0
    } else {
3458
0
      planError("invalid col equal list, leftBlockId:%" PRId64, ((SColumnNode*)pOp->pLeft)->dataBlockId);
3459
0
      return TSDB_CODE_PLAN_INTERNAL_ERROR;
3460
0
    }
3461
0
    if (TSDB_CODE_SUCCESS == code) {
3462
0
      pNew = NULL;
3463
0
      if (leftBlkId == ((SColumnNode*)pOp->pRight)->dataBlockId) {
3464
0
        code = nodesCloneNode(pOp->pRight, &pNew);
3465
0
        if (TSDB_CODE_SUCCESS == code) {
3466
0
          code = nodesListMakeStrictAppend(ppLeft, pNew);
3467
0
        }
3468
0
      } else if (rightBlkId == ((SColumnNode*)pOp->pRight)->dataBlockId) {
3469
0
        code = nodesCloneNode(pOp->pRight, &pNew);
3470
0
        if (TSDB_CODE_SUCCESS == code) {
3471
0
          code = nodesListMakeStrictAppend(ppRight, pNew);
3472
0
        }
3473
0
      } else {
3474
0
        planError("invalid col equal list, rightBlockId:%" PRId64, ((SColumnNode*)pOp->pRight)->dataBlockId);
3475
0
        return TSDB_CODE_PLAN_INTERNAL_ERROR;
3476
0
      }
3477
0
    }
3478
0
  } else if (QUERY_NODE_LOGIC_CONDITION == nodeType(pEqCond) &&
3479
0
             ((SLogicConditionNode*)pEqCond)->condType == LOGIC_COND_TYPE_AND) {
3480
0
    SLogicConditionNode* pLogic = (SLogicConditionNode*)pEqCond;
3481
0
    SNode*               pNode = NULL;
3482
0
    FOREACH(pNode, pLogic->pParameterList) {
3483
0
      int32_t code = setColEqList(pNode, leftBlkId, rightBlkId, ppLeft, ppRight);
3484
0
      if (code) {
3485
0
        return code;
3486
0
      }
3487
0
    }
3488
0
  } else {
3489
0
    planError("invalid col equal cond, type:%d", nodeType(pEqCond));
3490
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
3491
0
  }
3492
3493
0
  return code;
3494
0
}
3495
3496
static int32_t setMergeJoinPrimColEqCond(SNode* pEqCond, int32_t subType, int64_t leftBlkId, int64_t rightBlkId,
3497
0
                                         SSortMergeJoinPhysiNode* pJoin, SJoinLogicNode* pJoinLogicNode) {
3498
0
  int32_t code = 0;
3499
0
  if (QUERY_NODE_OPERATOR == nodeType(pEqCond)) {
3500
0
    SOperatorNode* pOp = (SOperatorNode*)pEqCond;
3501
0
    if (pOp->opType != OP_TYPE_EQUAL && JOIN_STYPE_ASOF != subType) {
3502
0
      planError("invalid primary cond opType, opType:%d", pOp->opType);
3503
0
      return TSDB_CODE_PLAN_INTERNAL_ERROR;
3504
0
    }
3505
3506
    // Track whether the LEFT operand already set asofOpType.  When two tables
3507
    // share the same column name (e.g. both have "ts"), doSetSlotId may
3508
    // incorrectly map the RIGHT operand to the left block too, causing the
3509
    // RIGHT branch below to overwrite asofOpType with the reversed operator.
3510
    // Giving priority to the LEFT operand preserves the correct direction.
3511
0
    bool asofOpSetFromLeft = false;
3512
3513
0
    switch (nodeType(pOp->pLeft)) {
3514
0
      case QUERY_NODE_COLUMN: {
3515
0
        SColumnNode* pCol = (SColumnNode*)pOp->pLeft;
3516
0
        if (leftBlkId == pCol->dataBlockId) {
3517
0
          pJoin->leftPrimSlotId = pCol->slotId;
3518
0
          pJoin->asofOpType = pOp->opType;
3519
0
          asofOpSetFromLeft = true;
3520
0
        } else if (rightBlkId == pCol->dataBlockId) {
3521
0
          pJoin->rightPrimSlotId = pCol->slotId;
3522
0
        } else {
3523
0
          planError("invalid primary key col equal cond, leftBlockId:%" PRId64, pCol->dataBlockId);
3524
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
3525
0
        }
3526
0
        break;
3527
0
      }
3528
0
      case QUERY_NODE_VALUE: {
3529
0
        if (pJoinLogicNode && pJoinLogicNode->leftConstPrimGot) {
3530
0
          pJoin->leftPrimExpr = NULL;
3531
0
          code = nodesCloneNode(pOp->pLeft, &pJoin->leftPrimExpr);
3532
0
          break;
3533
0
        }
3534
        
3535
0
        planError("value node got in prim eq left cond, rightType:%d", pOp->pRight ? nodeType(pOp->pRight) : 0);
3536
0
        return TSDB_CODE_PLAN_INTERNAL_ERROR;
3537
0
      }
3538
0
      case QUERY_NODE_FUNCTION: {
3539
0
        SFunctionNode* pFunc = (SFunctionNode*)pOp->pLeft;
3540
0
        if (FUNCTION_TYPE_TIMETRUNCATE != pFunc->funcType &&
3541
0
            FUNCTION_TYPE_TIMESTAMP_SCALE != pFunc->funcType) {
3542
0
          planError("invalid primary cond left function type, leftFuncType:%d", pFunc->funcType);
3543
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
3544
0
        }
3545
0
        SNode* pParam = nodesListGetNode(pFunc->pParameterList, 0);
3546
0
        if (QUERY_NODE_COLUMN != nodeType(pParam)) {
3547
0
          planError("invalid primary cond left function param type, leftParamType:%d", nodeType(pParam));
3548
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
3549
0
        }
3550
0
        SColumnNode* pCol = (SColumnNode*)pParam;
3551
0
        if (leftBlkId == pCol->dataBlockId) {
3552
0
          pJoin->leftPrimSlotId = pCol->slotId;
3553
0
          pJoin->asofOpType = pOp->opType;
3554
0
          asofOpSetFromLeft = true;
3555
0
          pJoin->leftPrimExpr = NULL;
3556
0
          code = nodesCloneNode((SNode*)pFunc, &pJoin->leftPrimExpr);
3557
0
        } else if (rightBlkId == pCol->dataBlockId) {
3558
0
          pJoin->rightPrimSlotId = pCol->slotId;
3559
0
          pJoin->rightPrimExpr = NULL;
3560
0
          code = nodesCloneNode((SNode*)pFunc, &pJoin->rightPrimExpr);
3561
0
        } else {
3562
0
          planError("invalid primary key col equal cond, leftBlockId:%" PRId64, pCol->dataBlockId);
3563
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
3564
0
        }
3565
0
        break;
3566
0
      }
3567
0
      default: {
3568
0
        SNodeList* pCols = NULL;
3569
0
        code = nodesCollectColumnsFromNode(pOp->pLeft, NULL, COLLECT_COL_TYPE_ALL, &pCols);
3570
0
        if (TSDB_CODE_SUCCESS != code || NULL == pCols || 0 == LIST_LENGTH(pCols)) {
3571
0
          nodesDestroyList(pCols);
3572
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
3573
0
        }
3574
0
        SColumnNode* pCol = (SColumnNode*)nodesListGetNode(pCols, 0);
3575
0
        for (SListCell* pCell = pCols->pHead; pCell; pCell = pCell->pNext) {
3576
0
          SColumnNode* pExprCol = (SColumnNode*)pCell->pNode;
3577
0
          if (pExprCol->dataBlockId != pCol->dataBlockId) {
3578
0
            nodesDestroyList(pCols);
3579
0
            return TSDB_CODE_PLAN_INTERNAL_ERROR;
3580
0
          }
3581
0
        }
3582
0
        if (leftBlkId == pCol->dataBlockId) {
3583
0
          pJoin->leftPrimSlotId = pCol->slotId;
3584
0
          pJoin->asofOpType = pOp->opType;
3585
0
          asofOpSetFromLeft = true;
3586
0
          code = nodesCloneNode(pOp->pLeft, &pJoin->leftPrimExpr);
3587
0
        } else if (rightBlkId == pCol->dataBlockId) {
3588
0
          pJoin->rightPrimSlotId = pCol->slotId;
3589
0
          code = nodesCloneNode(pOp->pLeft, &pJoin->rightPrimExpr);
3590
0
        } else {
3591
0
          code = TSDB_CODE_PLAN_INTERNAL_ERROR;
3592
0
        }
3593
0
        nodesDestroyList(pCols);
3594
0
        break;
3595
0
      }
3596
0
    }
3597
0
    if (TSDB_CODE_SUCCESS != code) {
3598
0
      return code;
3599
0
    }
3600
0
    switch (nodeType(pOp->pRight)) {
3601
0
      case QUERY_NODE_COLUMN: {
3602
0
        SColumnNode* pCol = (SColumnNode*)pOp->pRight;
3603
0
        if (leftBlkId == pCol->dataBlockId && !asofOpSetFromLeft) {
3604
0
          pJoin->leftPrimSlotId = pCol->slotId;
3605
0
          pJoin->asofOpType = getAsofJoinReverseOp(pOp->opType);
3606
0
        } else if (rightBlkId == pCol->dataBlockId) {
3607
0
          pJoin->rightPrimSlotId = pCol->slotId;
3608
0
        } else if (leftBlkId != pCol->dataBlockId) {
3609
0
          planError("invalid primary key col equal cond, rightBlockId:%" PRId64, pCol->dataBlockId);
3610
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
3611
0
        }
3612
0
        break;
3613
0
      }
3614
0
      case QUERY_NODE_VALUE: {
3615
0
        if (pJoinLogicNode && pJoinLogicNode->rightConstPrimGot) {
3616
0
          pJoin->rightPrimExpr = NULL;
3617
0
          code = nodesCloneNode(pOp->pRight, &pJoin->rightPrimExpr);
3618
0
          break;
3619
0
        }
3620
        
3621
0
        planError("value node got in prim eq right cond, leftType:%d", pOp->pLeft ? nodeType(pOp->pLeft) : 0);
3622
0
        return TSDB_CODE_PLAN_INTERNAL_ERROR;
3623
0
      }      
3624
0
      case QUERY_NODE_FUNCTION: {
3625
0
        SFunctionNode* pFunc = (SFunctionNode*)pOp->pRight;
3626
0
        if (FUNCTION_TYPE_TIMETRUNCATE != pFunc->funcType &&
3627
0
            FUNCTION_TYPE_TIMESTAMP_SCALE != pFunc->funcType) {
3628
0
          planError("invalid primary cond right function type, rightFuncType:%d", pFunc->funcType);
3629
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
3630
0
        }
3631
0
        SNode* pParam = nodesListGetNode(pFunc->pParameterList, 0);
3632
0
        if (QUERY_NODE_COLUMN != nodeType(pParam)) {
3633
0
          planError("invalid primary cond right function param type, rightParamType:%d", nodeType(pParam));
3634
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
3635
0
        }
3636
0
        SColumnNode* pCol = (SColumnNode*)pParam;
3637
0
        if (leftBlkId == pCol->dataBlockId && !asofOpSetFromLeft) {
3638
0
          pJoin->leftPrimSlotId = pCol->slotId;
3639
0
          pJoin->asofOpType = getAsofJoinReverseOp(pOp->opType);
3640
0
          pJoin->leftPrimExpr = NULL;
3641
0
          code = nodesCloneNode((SNode*)pFunc, &pJoin->leftPrimExpr);
3642
0
        } else if (rightBlkId == pCol->dataBlockId) {
3643
0
          pJoin->rightPrimSlotId = pCol->slotId;
3644
0
          pJoin->rightPrimExpr = NULL;
3645
0
          code = nodesCloneNode((SNode*)pFunc, &pJoin->rightPrimExpr);
3646
0
        } else if (leftBlkId != pCol->dataBlockId) {
3647
0
          planError("invalid primary key col equal cond, rightBlockId:%" PRId64, pCol->dataBlockId);
3648
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
3649
0
        }
3650
0
        break;
3651
0
      }
3652
0
      default: {
3653
0
        SNodeList* pCols = NULL;
3654
0
        code = nodesCollectColumnsFromNode(pOp->pRight, NULL, COLLECT_COL_TYPE_ALL, &pCols);
3655
0
        if (TSDB_CODE_SUCCESS != code || NULL == pCols || 0 == LIST_LENGTH(pCols)) {
3656
0
          nodesDestroyList(pCols);
3657
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
3658
0
        }
3659
0
        SColumnNode* pCol = (SColumnNode*)nodesListGetNode(pCols, 0);
3660
0
        if (leftBlkId == pCol->dataBlockId && !asofOpSetFromLeft) {
3661
0
          pJoin->leftPrimSlotId = pCol->slotId;
3662
0
          pJoin->asofOpType = getAsofJoinReverseOp(pOp->opType);
3663
0
          code = nodesCloneNode(pOp->pRight, &pJoin->leftPrimExpr);
3664
0
        } else if (rightBlkId == pCol->dataBlockId) {
3665
0
          pJoin->rightPrimSlotId = pCol->slotId;
3666
0
          code = nodesCloneNode(pOp->pRight, &pJoin->rightPrimExpr);
3667
0
        } else {
3668
0
          planError("invalid primary key col equal cond, rightBlockId:%" PRId64, pCol->dataBlockId);
3669
0
          code = TSDB_CODE_PLAN_INTERNAL_ERROR;
3670
0
        }
3671
0
        nodesDestroyList(pCols);
3672
0
        break;
3673
0
      }
3674
0
    }
3675
0
  } else {
3676
0
    planError("invalid primary key col equal cond, type:%d", nodeType(pEqCond));
3677
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
3678
0
  }
3679
3680
0
  return code;
3681
0
}
3682
3683
0
static int32_t removePrimColFromJoinTargets(SNodeList* pTargets, SValueNode* pPrimExpr, SColumnNode** ppRemoved) {
3684
0
  int32_t code = TSDB_CODE_SUCCESS;
3685
0
  SNode* pNode = NULL;
3686
0
  FOREACH(pNode, pTargets) {
3687
0
    SColumnNode* pCol = (SColumnNode*)pNode;
3688
0
    if (0 == strcmp(pCol->tableAlias, pPrimExpr->node.srcTable) && 0 == strcmp(pCol->colName, pPrimExpr->node.aliasName)) {
3689
0
      code = nodesCloneNode(pNode, (SNode**)ppRemoved);
3690
0
      ERASE_NODE(pTargets);
3691
0
      break;
3692
0
    }
3693
0
  }
3694
3695
0
  return code;
3696
0
}
3697
3698
0
static int32_t appendPrimColToJoinTargets(SSortMergeJoinPhysiNode* pJoin, SColumnNode** ppTarget, STargetNode* primExpr, int64_t blkId) {
3699
0
  SColumnNode* pCol = *ppTarget;
3700
0
  if (TSDB_DATA_TYPE_TIMESTAMP != pCol->node.resType.type) {
3701
0
    planError("primary key output type is not ts, type:%d", pCol->node.resType.type);
3702
0
    return TSDB_CODE_PAR_PRIM_KEY_MUST_BE_TS;
3703
0
  }
3704
0
  pCol->dataBlockId = blkId;
3705
0
  pCol->slotId = primExpr->slotId;
3706
0
  int32_t code = nodesListMakeStrictAppend(&pJoin->pTargets, (SNode *)pCol);
3707
0
  if (TSDB_CODE_SUCCESS == code) {
3708
0
    *ppTarget = NULL;
3709
0
  }
3710
3711
0
  return code;
3712
0
}
3713
3714
0
static const char* getLogicNodeTableAlias(const SLogicNode* pLogicNode) {
3715
0
  if (NULL == pLogicNode) {
3716
0
    return NULL;
3717
0
  }
3718
3719
0
  const SNodeList* pCols = NULL;
3720
0
  if (QUERY_NODE_LOGIC_PLAN_SCAN == nodeType(pLogicNode)) {
3721
0
    pCols = ((const SScanLogicNode*)pLogicNode)->pScanCols;
3722
0
  } else if (QUERY_NODE_LOGIC_PLAN_VIRTUAL_TABLE_SCAN == nodeType(pLogicNode)) {
3723
0
    pCols = ((const SVirtualScanLogicNode*)pLogicNode)->pScanCols;
3724
0
  }
3725
3726
0
  const SNode* pNode = NULL;
3727
0
  FOREACH(pNode, pCols) {
3728
0
    if (QUERY_NODE_COLUMN == nodeType(pNode) && '\0' != ((const SColumnNode*)pNode)->tableAlias[0]) {
3729
0
      return ((const SColumnNode*)pNode)->tableAlias;
3730
0
    }
3731
0
  }
3732
0
  FOREACH(pNode, pLogicNode->pTargets) {
3733
0
    if (QUERY_NODE_COLUMN == nodeType(pNode) && '\0' != ((const SColumnNode*)pNode)->tableAlias[0]) {
3734
0
      return ((const SColumnNode*)pNode)->tableAlias;
3735
0
    }
3736
0
  }
3737
0
  return NULL;
3738
0
}
3739
3740
0
static const char* getLogicNodeTargetAlias(const SLogicNode* pLogicNode) {
3741
0
  if (NULL != pLogicNode && QUERY_NODE_LOGIC_PLAN_PROJECT == nodeType(pLogicNode)) {
3742
0
    const char* pStmtName = ((const SProjectLogicNode*)pLogicNode)->stmtName;
3743
0
    if ('\0' != pStmtName[0]) {
3744
0
      return pStmtName;
3745
0
    }
3746
0
  }
3747
0
  return getLogicNodeTableAlias(pLogicNode);
3748
0
}
3749
3750
static void getJoinChildTableAliases(const SJoinLogicNode* pJoinLogicNode, const char** ppLeftAlias,
3751
0
                                     const char** ppRightAlias) {
3752
0
  const SLogicNode* pLeftLogic =
3753
0
      (const SLogicNode*)nodesListGetNode(pJoinLogicNode->node.pChildren, 0);
3754
0
  const SLogicNode* pRightLogic =
3755
0
      (const SLogicNode*)nodesListGetNode(pJoinLogicNode->node.pChildren, 1);
3756
0
  *ppLeftAlias = getLogicNodeTableAlias(pLeftLogic);
3757
0
  *ppRightAlias = getLogicNodeTableAlias(pRightLogic);
3758
0
}
3759
3760
static int32_t setJoinNodeSlotId(SPhysiPlanContext* pCxt, const SJoinLogicNode* pJoin, int64_t leftDataBlockId,
3761
0
                                 int64_t rightDataBlockId, SNode* pNode, SNode** pOutput) {
3762
  // Conditions are translated with the aliases exposed by the child scan
3763
  // targets.  For subqueries, stmtName (the outer alias) may differ from
3764
  // those target aliases; using it here makes both TIMETRUNCATE join sides
3765
  // resolve to the wrong descriptor and ends in "Planner slot key not found".
3766
0
  const char* leftAlias = NULL;
3767
0
  const char* rightAlias = NULL;
3768
0
  getJoinChildTableAliases(pJoin, &leftAlias, &rightAlias);
3769
0
  return setNodeSlotIdWithAliases(pCxt, leftDataBlockId, rightDataBlockId, leftAlias, rightAlias, pNode, pOutput);
3770
0
}
3771
3772
0
static bool getJoinExprTableAlias(SNode* pExpr, char* pAlias, int32_t aliasLen) {
3773
0
  SNodeList* pCols = NULL;
3774
0
  if (NULL == pExpr || TSDB_CODE_SUCCESS != nodesCollectColumnsFromNode(pExpr, NULL, COLLECT_COL_TYPE_ALL, &pCols)) {
3775
0
    return false;
3776
0
  }
3777
3778
0
  bool   found = false;
3779
0
  SNode* pNode = NULL;
3780
0
  FOREACH(pNode, pCols) {
3781
0
    SColumnNode* pCol = (SColumnNode*)pNode;
3782
0
    if ('\0' != pCol->tableAlias[0]) {
3783
0
      tstrncpy(pAlias, pCol->tableAlias, aliasLen);
3784
0
      found = true;
3785
0
      break;
3786
0
    }
3787
0
  }
3788
0
  nodesDestroyList(pCols);
3789
0
  return found;
3790
0
}
3791
3792
0
static bool getJoinCondSideAlias(SNode* pCond, int32_t childIndex, char* pAlias, int32_t aliasLen) {
3793
0
  if (NULL == pCond) return false;
3794
0
  if (QUERY_NODE_OPERATOR == nodeType(pCond)) {
3795
0
    SOperatorNode* pOp = (SOperatorNode*)pCond;
3796
0
    return getJoinExprTableAlias(0 == childIndex ? pOp->pLeft : pOp->pRight, pAlias, aliasLen);
3797
0
  }
3798
0
  if (QUERY_NODE_LOGIC_CONDITION == nodeType(pCond)) {
3799
0
    SNode* pNode = NULL;
3800
0
    FOREACH(pNode, ((SLogicConditionNode*)pCond)->pParameterList) {
3801
0
      if (getJoinCondSideAlias(pNode, childIndex, pAlias, aliasLen)) return true;
3802
0
    }
3803
0
  }
3804
0
  return false;
3805
0
}
3806
3807
static int32_t setJoinTargetListSlotId(SPhysiPlanContext* pCxt, const SJoinLogicNode* pJoin,
3808
                                       int64_t leftDataBlockId, int64_t rightDataBlockId,
3809
0
                                       const SNodeList* pList, SNodeList** pOutput) {
3810
0
  if (NULL == pList) return TSDB_CODE_SUCCESS;
3811
3812
0
  SNodeList* pRes = NULL;
3813
0
  int32_t    code = nodesCloneList(pList, &pRes);
3814
0
  if (TSDB_CODE_SUCCESS != code) return code;
3815
3816
0
  char leftAlias[TSDB_TABLE_NAME_LEN] = {0};
3817
0
  char rightAlias[TSDB_TABLE_NAME_LEN] = {0};
3818
0
  SNode* pEqCond = NULL != pJoin->pPrimKeyEqCond ? pJoin->pPrimKeyEqCond : pJoin->pColEqCond;
3819
0
  const char* pLeftAlias = getJoinCondSideAlias(pEqCond, 0, leftAlias, sizeof(leftAlias)) ? leftAlias : NULL;
3820
0
  const char* pRightAlias = getJoinCondSideAlias(pEqCond, 1, rightAlias, sizeof(rightAlias)) ? rightAlias : NULL;
3821
0
  if (pJoin->isCrossJoin &&
3822
0
      (NULL == pLeftAlias || NULL == pRightAlias || 0 == strcmp(pLeftAlias, pRightAlias))) {
3823
0
    const SLogicNode* pLeftLogic = (const SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 0);
3824
0
    const SLogicNode* pRightLogic = (const SLogicNode*)nodesListGetNode(pJoin->node.pChildren, 1);
3825
0
    pLeftAlias = getLogicNodeTargetAlias(pLeftLogic);
3826
0
    pRightAlias = getLogicNodeTargetAlias(pRightLogic);
3827
0
  }
3828
0
  SSetSlotIdCxt cxt = {
3829
0
      .errCode = TSDB_CODE_SUCCESS,
3830
0
      .pLeftHash = taosArrayGetP(pCxt->pLocationHelper, leftDataBlockId),
3831
0
      .pLeftProjIdxHash = taosArrayGetP(pCxt->pProjIdxLocHelper, leftDataBlockId),
3832
0
      .pRightHash = taosArrayGetP(pCxt->pLocationHelper, rightDataBlockId),
3833
0
      .pRightProdIdxHash = taosArrayGetP(pCxt->pProjIdxLocHelper, rightDataBlockId),
3834
0
      .pLeftTableAlias = pLeftAlias,
3835
0
      .pRightTableAlias = pRightAlias};
3836
0
  nodesWalkExprs(pRes, doSetSlotId, &cxt);
3837
0
  if (TSDB_CODE_SUCCESS != cxt.errCode) {
3838
0
    nodesDestroyList(pRes);
3839
0
    return cxt.errCode;
3840
0
  }
3841
0
  *pOutput = pRes;
3842
0
  return TSDB_CODE_SUCCESS;
3843
0
}
3844
3845
static int32_t addCrossJoinChildAliasKeys(SPhysiPlanContext* pCxt, const SLogicNode* pLogicChild,
3846
0
                                          const SDataBlockDescNode* pDesc) {
3847
0
  if (NULL == pLogicChild || NULL == pLogicChild->pTargets || NULL == pDesc || NULL == pDesc->pSlots) {
3848
0
    return TSDB_CODE_SUCCESS;
3849
0
  }
3850
3851
0
  SHashObj* pHash = taosArrayGetP(pCxt->pLocationHelper, pDesc->dataBlockId);
3852
0
  int32_t targetNum = LIST_LENGTH(pLogicChild->pTargets);
3853
0
  int32_t slotNum = LIST_LENGTH(pDesc->pSlots);
3854
0
  for (int32_t i = 0; NULL != pHash && i < targetNum && i < slotNum; ++i) {
3855
0
    SNode* pTarget = nodesListGetNode(pLogicChild->pTargets, i);
3856
0
    SSlotDescNode* pSlot = (SSlotDescNode*)nodesListGetNode(pDesc->pSlots, i);
3857
0
    if (QUERY_NODE_COLUMN != nodeType(pTarget) || '\0' == ((SColumnNode*)pTarget)->tableAlias[0]) {
3858
0
      continue;
3859
0
    }
3860
3861
0
    char* pKey = NULL;
3862
0
    int32_t keyLen = 0;
3863
0
    int32_t keyCap = 0;
3864
0
    int32_t code = getSlotKey(pTarget, NULL, &pKey, &keyLen, 0, &keyCap);
3865
0
    if (TSDB_CODE_SUCCESS == code) {
3866
0
      code = putSlotToHashImpl(pDesc->dataBlockId, pSlot->slotId, pKey, keyLen, pHash);
3867
0
    }
3868
0
    taosMemoryFree(pKey);
3869
0
    if (TSDB_CODE_SUCCESS != code) {
3870
0
      return code;
3871
0
    }
3872
0
  }
3873
0
  return TSDB_CODE_SUCCESS;
3874
0
}
3875
3876
0
static bool fqJoinTargetExprExistsBySlotKey(const SNodeList* pTargets, SNode* pExpr) {
3877
0
  if (NULL == pTargets || NULL == pExpr) {
3878
0
    return false;
3879
0
  }
3880
3881
0
  char*   pExprKey = NULL;
3882
0
  int32_t exprKeyLen = 0;
3883
0
  int32_t exprKeyCap = 0;
3884
0
  int32_t code = getSlotKey(pExpr, NULL, &pExprKey, &exprKeyLen, 0, &exprKeyCap);
3885
0
  if (TSDB_CODE_SUCCESS != code) {
3886
0
    planError("FQ-joinCondTargets: build expr slot key failed exprType=%d code=0x%x",
3887
0
              (int)nodeType(pExpr), code);
3888
0
    return false;
3889
0
  }
3890
3891
0
  bool exists = false;
3892
0
  SNode* pNode = NULL;
3893
0
  FOREACH(pNode, pTargets) {
3894
0
    char*   pTargetKey = NULL;
3895
0
    int32_t targetKeyLen = 0;
3896
0
    int32_t targetKeyCap = 0;
3897
0
    code = getSlotKey(pNode, NULL, &pTargetKey, &targetKeyLen, 0, &targetKeyCap);
3898
0
    if (TSDB_CODE_SUCCESS == code &&
3899
0
        exprKeyLen == targetKeyLen &&
3900
0
        0 == memcmp(pExprKey, pTargetKey, exprKeyLen)) {
3901
0
      exists = true;
3902
0
    }
3903
0
    taosMemoryFree(pTargetKey);
3904
0
    if (exists || TSDB_CODE_SUCCESS != code) {
3905
0
      if (TSDB_CODE_SUCCESS != code) {
3906
0
        planError("FQ-joinCondTargets: build target slot key failed exprType=%d code=0x%x",
3907
0
                  (int)nodeType(pNode), code);
3908
0
      }
3909
0
      break;
3910
0
    }
3911
0
  }
3912
3913
0
  taosMemoryFree(pExprKey);
3914
0
  return exists;
3915
0
}
3916
3917
static int32_t fqAppendJoinConditionTargetsFromNode(SPhysiPlanContext* pCxt,
3918
                                                    SNode* pCond,
3919
                                                    int64_t leftDataBlockId,
3920
                                                    int64_t rightDataBlockId,
3921
                                                    SSHashObj** pTables,
3922
                                                    SNodeList* pTargets,
3923
0
                                                    int32_t* pAppended) {
3924
0
  SNodeList* pCondCols = NULL;
3925
0
  int32_t code = nodesCollectColumnsFromNode(pCond, NULL, COLLECT_COL_TYPE_ALL,
3926
0
                                            &pCondCols);
3927
0
  if (TSDB_CODE_SUCCESS != code) {
3928
0
    nodesDestroyList(pCondCols);
3929
0
    return code;
3930
0
  }
3931
3932
0
  int32_t appended = 0;
3933
0
  SNode* pNode = NULL;
3934
0
  FOREACH(pNode, pCondCols) {
3935
0
    SColumnNode* pCol = (SColumnNode*)pNode;
3936
0
    size_t aliasLen = strlen(pCol->tableAlias);
3937
0
    bool left = NULL != tSimpleHashGet(pTables[0], pCol->tableAlias, aliasLen);
3938
0
    bool right = NULL != tSimpleHashGet(pTables[1], pCol->tableAlias, aliasLen);
3939
0
    int64_t firstBlockId = leftDataBlockId;
3940
0
    int64_t secondBlockId = rightDataBlockId;
3941
    // A bare-name fallback must stay within the column's owning input.
3942
0
    if (left != right) {
3943
0
      firstBlockId = left ? leftDataBlockId : rightDataBlockId;
3944
0
      secondBlockId = -1;
3945
0
    }
3946
3947
0
    SNode* pBound = NULL;
3948
0
    code = setNodeSlotId(pCxt, firstBlockId, secondBlockId, pNode, &pBound);
3949
0
    if (TSDB_CODE_SUCCESS != code) {
3950
0
      planError("FQ-joinCondTargets: bind condition columns failed "
3951
0
                "leftBlockId=%" PRId64 " rightBlockId=%" PRId64
3952
0
                " code=0x%x cond=%p",
3953
0
                leftDataBlockId, rightDataBlockId, code, pCond);
3954
0
      break;
3955
0
    }
3956
0
    if (fqJoinTargetExprExistsBySlotKey(pTargets, pBound)) {
3957
0
      nodesDestroyNode(pBound);
3958
0
      continue;
3959
0
    }
3960
0
    code = nodesListStrictAppend(pTargets, pBound);
3961
0
    if (TSDB_CODE_SUCCESS != code) {
3962
0
      planError("FQ-joinCondTargets: append hidden target failed col=%s "
3963
0
                "tableAlias=%s code=0x%x", pCol->colName, pCol->tableAlias,
3964
0
                code);
3965
0
      break;
3966
0
    }
3967
0
    ++appended;
3968
0
    SColumnNode* pBoundCol = (SColumnNode*)pBound;
3969
0
    planDebug("FQ-joinCondTargets: appended hidden condition column col=%s "
3970
0
              "alias=%s tableAlias=%s dataBlockId=%" PRId64 " slotId=%d",
3971
0
              pBoundCol->colName, pBoundCol->node.aliasName,
3972
0
              pBoundCol->tableAlias, pBoundCol->dataBlockId, pBoundCol->slotId);
3973
0
  }
3974
0
  *pAppended += appended;
3975
0
  planDebug("FQ-joinCondTargets: leftBlockId=%" PRId64 " rightBlockId=%" PRId64
3976
0
            " appended=%d condCols=%d finalTargets=%d cond=%p",
3977
0
            leftDataBlockId, rightDataBlockId, appended,
3978
0
            LIST_LENGTH(pCondCols), LIST_LENGTH(pTargets), pCond);
3979
0
  nodesDestroyList(pCondCols);
3980
0
  return code;
3981
0
}
3982
3983
static int32_t fqAppendJoinConditionTargets(SPhysiPlanContext* pCxt,
3984
                                            SJoinLogicNode* pJoinLogicNode,
3985
                                            int64_t leftDataBlockId,
3986
                                            int64_t rightDataBlockId,
3987
0
                                            SNodeList* pTargets) {
3988
0
  if (NULL == pCxt || NULL == pJoinLogicNode || NULL == pTargets) {
3989
0
    return TSDB_CODE_SUCCESS;
3990
0
  }
3991
3992
0
  SNode* pConditions[] = {pJoinLogicNode->pFullOnCond,
3993
0
                         pJoinLogicNode->pColOnCond,
3994
0
                         pJoinLogicNode->node.pConditions};
3995
0
  if (NULL == pConditions[0] && NULL == pConditions[1] &&
3996
0
      NULL == pConditions[2]) {
3997
0
    return TSDB_CODE_SUCCESS;
3998
0
  }
3999
4000
0
  int32_t code = TSDB_CODE_SUCCESS;
4001
0
  int32_t appended = 0;
4002
0
  SSHashObj* pTables[2] = {NULL, NULL};
4003
0
  for (int32_t i = 0; i < 2 && TSDB_CODE_SUCCESS == code; ++i) {
4004
0
    SNode* pChild = nodesListGetNode(pJoinLogicNode->node.pChildren, i);
4005
0
    code = collectTableAliasFromNodes(pChild, &pTables[i]);
4006
0
    if (TSDB_CODE_SUCCESS == code) {
4007
0
      code = collectSourceTableAliasFromNodes(pChild, &pTables[i]);
4008
0
    }
4009
0
  }
4010
4011
  // ON and WHERE predicates may read hidden columns from the JOIN output.
4012
0
  for (int32_t i = 0; i < 3 && TSDB_CODE_SUCCESS == code; ++i) {
4013
0
    if (NULL != pConditions[i]) {
4014
0
      code = fqAppendJoinConditionTargetsFromNode(pCxt, pConditions[i],
4015
0
                                                 leftDataBlockId,
4016
0
                                                 rightDataBlockId, pTables,
4017
0
                                                 pTargets, &appended);
4018
0
    }
4019
0
  }
4020
0
  planDebug("FQ-joinCondTargets: joinType=%d leftBlockId=%" PRId64
4021
0
            " rightBlockId=%" PRId64 " totalAppended=%d finalTargets=%d "
4022
0
            "fullOn=%p colOn=%p where=%p", (int)pJoinLogicNode->joinType,
4023
0
            leftDataBlockId, rightDataBlockId, appended, LIST_LENGTH(pTargets),
4024
0
            pConditions[0], pConditions[1], pConditions[2]);
4025
0
  tSimpleHashCleanup(pTables[0]);
4026
0
  tSimpleHashCleanup(pTables[1]);
4027
0
  return code;
4028
0
}
4029
static int32_t createMergeJoinPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren, SJoinLogicNode* pJoinLogicNode,
4030
0
                                        SPhysiNode** pPhyNode) {
4031
0
  bool constPrimEqCond =
4032
0
      NULL != pJoinLogicNode->pPrimKeyEqCond && QUERY_NODE_OPERATOR == nodeType(pJoinLogicNode->pPrimKeyEqCond) &&
4033
0
      QUERY_NODE_VALUE == nodeType(((SOperatorNode*)pJoinLogicNode->pPrimKeyEqCond)->pLeft) &&
4034
0
      QUERY_NODE_VALUE == nodeType(((SOperatorNode*)pJoinLogicNode->pPrimKeyEqCond)->pRight);
4035
0
  if (!IS_WINDOW_JOIN(pJoinLogicNode->subType) && !IS_ASOF_JOIN(pJoinLogicNode->subType) &&
4036
0
      (NULL == pJoinLogicNode->pPrimKeyEqCond ||
4037
0
       ((!constPrimEqCond) &&
4038
0
        (pJoinLogicNode->noPrimKeyEqCond || pJoinLogicNode->leftNoOrderedSubQuery ||
4039
0
         pJoinLogicNode->rightNoOrderedSubQuery)))) {
4040
0
    return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PAR_NOT_SUPPORT_JOIN,
4041
0
                               "Merge Join requires ordered inputs and primary timestamp equal condition");
4042
0
  }
4043
4044
0
  SSortMergeJoinPhysiNode* pJoin =
4045
0
      (SSortMergeJoinPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pJoinLogicNode, QUERY_NODE_PHYSICAL_PLAN_MERGE_JOIN);
4046
0
  if (NULL == pJoin) {
4047
0
    return terrno;
4048
0
  }
4049
4050
0
  SDataBlockDescNode* pLeftDesc = NULL;
4051
0
  SDataBlockDescNode* pRightDesc = NULL;
4052
0
  pJoin->joinType = pJoinLogicNode->joinType;
4053
0
  pJoin->subType = pJoinLogicNode->subType;
4054
0
  pJoin->pWindowOffset = NULL;
4055
0
  int32_t code = nodesCloneNode(pJoinLogicNode->pWindowOffset, &pJoin->pWindowOffset);
4056
0
  if (TSDB_CODE_SUCCESS == code) {
4057
0
    pJoin->pJLimit = NULL;
4058
0
    code = nodesCloneNode(pJoinLogicNode->pJLimit, (SNode**)&pJoin->pJLimit);
4059
0
  }
4060
0
  if (TSDB_CODE_SUCCESS == code) {
4061
0
    pJoin->node.inputTsOrder = pJoinLogicNode->node.inputTsOrder;
4062
0
    pJoin->seqWinGroup = pJoinLogicNode->seqWinGroup;
4063
0
    pJoin->grpJoin = pJoinLogicNode->grpJoin;
4064
0
    code = getJoinDataBlockDescNode(pChildren, 0, &pLeftDesc);
4065
0
  }
4066
4067
0
  if (TSDB_CODE_SUCCESS == code) {
4068
0
    code = getJoinDataBlockDescNode(pChildren, 1, &pRightDesc);
4069
0
  }
4070
4071
0
  if (TSDB_CODE_SUCCESS == code && NULL != pJoinLogicNode->pPrimKeyEqCond) {
4072
0
    code = setJoinNodeSlotId(pCxt, pJoinLogicNode, pLeftDesc->dataBlockId, pRightDesc->dataBlockId,
4073
0
                             pJoinLogicNode->pPrimKeyEqCond, &pJoin->pPrimKeyCond);
4074
0
    if (TSDB_CODE_SUCCESS == code) {
4075
0
      code = setMergeJoinPrimColEqCond(pJoin->pPrimKeyCond, pJoin->subType, pLeftDesc->dataBlockId,
4076
0
                                       pRightDesc->dataBlockId, pJoin, pJoinLogicNode);
4077
0
    }
4078
0
    if (TSDB_CODE_SUCCESS == code && NULL != pJoin->leftPrimExpr) {
4079
0
      code = addDataBlockSlot(pCxt, &pJoin->leftPrimExpr, pLeftDesc);
4080
0
    }
4081
0
    if (TSDB_CODE_SUCCESS == code && NULL != pJoin->rightPrimExpr) {
4082
0
      code = addDataBlockSlot(pCxt, &pJoin->rightPrimExpr, pRightDesc);
4083
0
    }
4084
0
  }
4085
4086
0
  if (TSDB_CODE_SUCCESS == code && NULL != pJoinLogicNode->addPrimEqCond) {
4087
0
    SNode* pPrimKeyCond = NULL;
4088
0
    code = setJoinNodeSlotId(pCxt, pJoinLogicNode, pLeftDesc->dataBlockId,
4089
0
                            pRightDesc->dataBlockId,
4090
0
                            pJoinLogicNode->addPrimEqCond, &pPrimKeyCond);
4091
0
    if (TSDB_CODE_SUCCESS == code) {
4092
0
      code = setMergeJoinPrimColEqCond(pPrimKeyCond, pJoin->subType, pLeftDesc->dataBlockId, pRightDesc->dataBlockId,
4093
0
                                       pJoin, NULL);
4094
0
    }
4095
0
    if (TSDB_CODE_SUCCESS == code && NULL != pJoin->leftPrimExpr) {
4096
0
      code = addDataBlockSlot(pCxt, &pJoin->leftPrimExpr, pLeftDesc);
4097
0
    }
4098
0
    if (TSDB_CODE_SUCCESS == code && NULL != pJoin->rightPrimExpr) {
4099
0
      code = addDataBlockSlot(pCxt, &pJoin->rightPrimExpr, pRightDesc);
4100
0
    }
4101
0
    if (TSDB_CODE_SUCCESS == code && NULL == pJoin->pPrimKeyCond) {
4102
0
      pJoin->pPrimKeyCond = pPrimKeyCond;
4103
0
      pPrimKeyCond = NULL;
4104
0
    }
4105
0
    nodesDestroyNode(pPrimKeyCond);
4106
0
  }
4107
4108
0
  SValueNode* pLeftPrimExpr = NULL, *pRightPrimExpr = NULL;
4109
0
  SColumnNode* pLeftTarget = NULL, *pRightTarget = NULL;
4110
0
  if (TSDB_CODE_SUCCESS == code && pJoinLogicNode->leftConstPrimGot && pJoin->leftPrimExpr 
4111
0
      && QUERY_NODE_VALUE == nodeType(((STargetNode*)pJoin->leftPrimExpr)->pExpr)) {
4112
0
    pLeftPrimExpr = (SValueNode*)((STargetNode*)pJoin->leftPrimExpr)->pExpr;
4113
0
    code = removePrimColFromJoinTargets(pJoinLogicNode->node.pTargets, pLeftPrimExpr, &pLeftTarget);
4114
0
  }
4115
4116
0
  if (TSDB_CODE_SUCCESS == code && pJoinLogicNode->rightConstPrimGot && pJoin->rightPrimExpr 
4117
0
      && QUERY_NODE_VALUE == nodeType(((STargetNode*)pJoin->rightPrimExpr)->pExpr)) {
4118
0
    pRightPrimExpr = (SValueNode*)((STargetNode*)pJoin->rightPrimExpr)->pExpr;
4119
0
    code = removePrimColFromJoinTargets(pJoinLogicNode->node.pTargets, pRightPrimExpr, &pRightTarget);
4120
0
  }
4121
4122
0
  if (TSDB_CODE_SUCCESS == code) {
4123
0
    code = setJoinTargetListSlotId(pCxt, pJoinLogicNode, pLeftDesc->dataBlockId, pRightDesc->dataBlockId,
4124
0
                                   pJoinLogicNode->node.pTargets, &pJoin->pTargets);
4125
0
  }
4126
4127
0
  if (TSDB_CODE_SUCCESS == code && pLeftPrimExpr && pLeftTarget) {
4128
0
    code = appendPrimColToJoinTargets(pJoin, &pLeftTarget, (STargetNode*)pJoin->leftPrimExpr, pLeftDesc->dataBlockId);
4129
0
  }
4130
0
  if (TSDB_CODE_SUCCESS == code && pRightPrimExpr && pRightTarget) {
4131
0
    code = appendPrimColToJoinTargets(pJoin, &pRightTarget, (STargetNode*)pJoin->rightPrimExpr, pRightDesc->dataBlockId);
4132
0
  }
4133
4134
0
  if (TSDB_CODE_SUCCESS == code && ((NULL != pJoinLogicNode->pColEqCond) || (NULL != pJoinLogicNode->pTagEqCond))) {
4135
0
    code = mergeJoinConds(&pJoinLogicNode->pColEqCond, &pJoinLogicNode->pTagEqCond);
4136
0
  }
4137
  // TODO set from input blocks for group algo
4138
  /*
4139
    if (TSDB_CODE_SUCCESS == code && NULL != pJoinLogicNode->pColEqCond) {
4140
      code = setNodeSlotId(pCxt, pLeftDesc->dataBlockId, pRightDesc->dataBlockId, pJoinLogicNode->pColEqCond,
4141
    &pJoin->pColEqCond);
4142
    }
4143
  */
4144
0
  if (TSDB_CODE_SUCCESS == code && NULL != pJoinLogicNode->pColEqCond) {
4145
0
    code = setJoinNodeSlotId(pCxt, pJoinLogicNode, pLeftDesc->dataBlockId, pRightDesc->dataBlockId,
4146
0
                             pJoinLogicNode->pColEqCond, &pJoin->pColEqCond);
4147
0
    if (TSDB_CODE_SUCCESS == code) {
4148
0
      code = setColEqList(pJoin->pColEqCond, pLeftDesc->dataBlockId, pRightDesc->dataBlockId, &pJoin->pEqLeft,
4149
0
                          &pJoin->pEqRight);
4150
0
    }
4151
0
  }
4152
4153
0
  if (TSDB_CODE_SUCCESS == code && ((NULL != pJoinLogicNode->pColOnCond) || (NULL != pJoinLogicNode->pTagOnCond))) {
4154
0
    code = mergeJoinConds(&pJoinLogicNode->pColOnCond, &pJoinLogicNode->pTagOnCond);
4155
0
  }
4156
  // TODO set from input blocks for group algo
4157
  /*
4158
    if (TSDB_CODE_SUCCESS == code && NULL != pJoinLogicNode->pColOnCond) {
4159
    code = setJoinNodeSlotId(pCxt, pJoinLogicNode, pLeftDesc->dataBlockId, pRightDesc->dataBlockId,
4160
                             pJoinLogicNode->pColOnCond, &pJoin->pColOnCond);
4161
    }
4162
  */
4163
4164
0
  if (TSDB_CODE_SUCCESS == code) {
4165
0
    code = fqAppendJoinConditionTargets(pCxt, pJoinLogicNode, pLeftDesc->dataBlockId, pRightDesc->dataBlockId,
4166
0
                                        pJoin->pTargets);
4167
0
  }
4168
4169
0
  if (TSDB_CODE_SUCCESS == code) {
4170
0
    code = addDataBlockSlots(pCxt, pJoin->pTargets, pJoin->node.pOutputDataBlockDesc);
4171
0
  }
4172
4173
0
  if (TSDB_CODE_SUCCESS == code && NULL != pJoinLogicNode->pFullOnCond) {
4174
0
    code = setNodeSlotId(pCxt, pJoin->node.pOutputDataBlockDesc->dataBlockId, -1, pJoinLogicNode->pFullOnCond,
4175
0
                         &pJoin->pFullOnCond);
4176
0
    planDebug("FQ-joinSlots: mergeJoin bind fullOnCond code=0x%x outputBlockId=%" PRId64 " cond=%p bound=%p",
4177
0
              code, pJoin->node.pOutputDataBlockDesc->dataBlockId, pJoinLogicNode->pFullOnCond, pJoin->pFullOnCond);
4178
0
  }
4179
4180
0
  if (TSDB_CODE_SUCCESS == code && NULL != pJoinLogicNode->pColOnCond) {
4181
0
    code = setNodeSlotId(pCxt, pJoin->node.pOutputDataBlockDesc->dataBlockId, -1, pJoinLogicNode->pColOnCond,
4182
0
                         &pJoin->pColOnCond);
4183
0
    planDebug("FQ-joinSlots: mergeJoin bind colOnCond code=0x%x outputBlockId=%" PRId64 " cond=%p bound=%p",
4184
0
              code, pJoin->node.pOutputDataBlockDesc->dataBlockId, pJoinLogicNode->pColOnCond, pJoin->pColOnCond);
4185
0
  }
4186
4187
0
  if (TSDB_CODE_SUCCESS == code) {
4188
0
    code = setConditionsSlotId(pCxt, (const SLogicNode*)pJoinLogicNode, (SPhysiNode*)pJoin);
4189
0
  }
4190
4191
0
  if (TSDB_CODE_SUCCESS == code) {
4192
0
    *pPhyNode = (SPhysiNode*)pJoin;
4193
0
  } else {
4194
0
    nodesDestroyNode((SNode*)pJoin);
4195
0
    nodesDestroyNode((SNode*)pLeftTarget);
4196
0
    nodesDestroyNode((SNode*)pRightTarget);
4197
0
  }
4198
4199
0
  return code;
4200
0
}
4201
4202
0
static int32_t createHashJoinKeyColFromPrimExpr(SPhysiPlanContext* pCxt, SNode* pExpr, SNode** ppCol) {
4203
0
  if (NULL == pExpr || NULL == ppCol) {
4204
0
    planError("Invalid hash join key target");
4205
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
4206
0
  }
4207
4208
0
  if (QUERY_NODE_FUNCTION == nodeType(pExpr)) {
4209
0
    SFunctionNode* pFunc = (SFunctionNode*)pExpr;
4210
0
    if (FUNCTION_TYPE_TIMETRUNCATE != pFunc->funcType &&
4211
0
        FUNCTION_TYPE_TIMESTAMP_SCALE != pFunc->funcType) {
4212
0
      planError("Invalid hash join key function type:%d", pFunc->funcType);
4213
0
      return TSDB_CODE_PLAN_INTERNAL_ERROR;
4214
0
    }
4215
0
    SNode* pParam = nodesListGetNode(pFunc->pParameterList, 0);
4216
0
    if (NULL == pParam || QUERY_NODE_COLUMN != nodeType(pParam)) {
4217
0
      return TSDB_CODE_PLAN_INTERNAL_ERROR;
4218
0
    }
4219
4220
0
    char* pName = NULL;
4221
0
    int32_t len = 0;
4222
0
    int32_t cap = 0;
4223
0
    int32_t code = getSlotKey(pExpr, NULL, &pName, &len, 0, &cap);
4224
0
    if (TSDB_CODE_SUCCESS != code) return code;
4225
0
    SHashObj* pHash = taosArrayGetP(pCxt->pLocationHelper, ((SColumnNode*)pParam)->dataBlockId);
4226
0
    SSlotIndex* pIndex = pHash ? taosHashGet(pHash, pName, len) : NULL;
4227
0
    taosMemoryFree(pName);
4228
0
    if (NULL == pIndex) {
4229
0
      return TSDB_CODE_PLAN_SLOT_NOT_FOUND;
4230
0
    }
4231
0
    if (NULL == pIndex->pSlotIdsInfo || 0 == taosArrayGetSize(pIndex->pSlotIdsInfo)) {
4232
0
      return TSDB_CODE_PLAN_SLOT_NOT_FOUND;
4233
0
    }
4234
0
    SSlotIdInfo* pSlotInfo = taosArrayGet(pIndex->pSlotIdsInfo, 0);
4235
0
    if (NULL == pSlotInfo) {
4236
0
      return TSDB_CODE_PLAN_SLOT_NOT_FOUND;
4237
0
    }
4238
4239
0
    SColumnNode* pCol = NULL;
4240
0
    code = nodesMakeNode(QUERY_NODE_COLUMN, (SNode**)&pCol);
4241
0
    if (TSDB_CODE_SUCCESS != code) return code;
4242
0
    pCol->dataBlockId = pIndex->dataBlockId;
4243
0
    pCol->slotId = pSlotInfo->slotId;
4244
0
    pCol->colType = COLUMN_TYPE_COLUMN;
4245
0
    pCol->node.resType = ((SExprNode*)pExpr)->resType;
4246
0
    tstrncpy(pCol->colName, ((SExprNode*)pExpr)->aliasName, TSDB_COL_NAME_LEN);
4247
0
    tstrncpy(pCol->node.aliasName, ((SExprNode*)pExpr)->aliasName, TSDB_COL_NAME_LEN);
4248
0
    tstrncpy(pCol->node.userAlias, ((SExprNode*)pExpr)->aliasName, TSDB_COL_NAME_LEN);
4249
0
    *ppCol = (SNode*)pCol;
4250
0
    return TSDB_CODE_SUCCESS;
4251
0
  }
4252
4253
0
  if (QUERY_NODE_TARGET != nodeType(pExpr)) {
4254
0
    planError("Invalid hash join key target type:%d", nodeType(pExpr));
4255
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
4256
0
  }
4257
0
  STargetNode* pTarget = (STargetNode*)pExpr;
4258
0
  if (NULL == pTarget->pExpr) return TSDB_CODE_PLAN_INTERNAL_ERROR;
4259
4260
0
  SColumnNode* pCol = NULL;
4261
0
  int32_t      code = nodesMakeNode(QUERY_NODE_COLUMN, (SNode**)&pCol);
4262
0
  if (TSDB_CODE_SUCCESS != code) {
4263
0
    return code;
4264
0
  }
4265
4266
0
  pCol->dataBlockId = pTarget->dataBlockId;
4267
0
  pCol->slotId = pTarget->slotId;
4268
0
  pCol->colType = COLUMN_TYPE_COLUMN;
4269
0
  pCol->node.resType = ((SExprNode*)pTarget->pExpr)->resType;
4270
0
  tstrncpy(pCol->colName, ((SExprNode*)pTarget->pExpr)->aliasName, TSDB_COL_NAME_LEN);
4271
0
  tstrncpy(pCol->node.aliasName, ((SExprNode*)pTarget->pExpr)->aliasName, TSDB_COL_NAME_LEN);
4272
0
  tstrncpy(pCol->node.userAlias, ((SExprNode*)pTarget->pExpr)->aliasName, TSDB_COL_NAME_LEN);
4273
0
  *ppCol = (SNode*)pCol;
4274
0
  return TSDB_CODE_SUCCESS;
4275
0
}
4276
4277
static int32_t getHashJoinNodeBlkId(SPhysiPlanContext* pCxt, SNode* pNode, int64_t leftBlkId, int64_t rightBlkId,
4278
0
                                    int64_t* pBlkId) {
4279
  // Columns have already been slot-bound; block zero is a valid input.
4280
0
  if (QUERY_NODE_COLUMN == nodeType(pNode) && ((SColumnNode*)pNode)->dataBlockId >= 0) {
4281
0
    int64_t blockId = ((SColumnNode*)pNode)->dataBlockId;
4282
0
    if (blockId != leftBlkId && blockId != rightBlkId) return TSDB_CODE_PLAN_NOT_SUPPORT_JOIN_COND;
4283
0
    *pBlkId = blockId;
4284
0
    return TSDB_CODE_SUCCESS;
4285
0
  }
4286
4287
0
  char*   pName = NULL;
4288
0
  int32_t len = 0;
4289
0
  int32_t cap = 0;
4290
0
  int32_t code = getSlotKey(pNode, NULL, &pName, &len, 0, &cap);
4291
0
  if (TSDB_CODE_SUCCESS != code) return code;
4292
4293
0
  int64_t blockId = -1;
4294
0
  SHashObj* pHash = taosArrayGetP(pCxt->pLocationHelper, leftBlkId);
4295
0
  if (NULL != pHash && NULL != taosHashGet(pHash, pName, len)) {
4296
0
    blockId = leftBlkId;
4297
0
  } else {
4298
0
    pHash = taosArrayGetP(pCxt->pLocationHelper, rightBlkId);
4299
0
    if (NULL != pHash && NULL != taosHashGet(pHash, pName, len)) blockId = rightBlkId;
4300
0
  }
4301
0
  taosMemoryFree(pName);
4302
0
  if (blockId < 0) return TSDB_CODE_PLAN_SLOT_NOT_FOUND;
4303
4304
0
  *pBlkId = blockId;
4305
0
  return TSDB_CODE_SUCCESS;
4306
0
}
4307
4308
static int32_t getHashJoinExprBlkId(SPhysiPlanContext* pCxt, SNode* pExpr, int64_t leftBlkId, int64_t rightBlkId,
4309
0
                                    int64_t* pBlkId) {
4310
0
  if (QUERY_NODE_COLUMN == nodeType(pExpr)) {
4311
0
    return getHashJoinNodeBlkId(pCxt, pExpr, leftBlkId, rightBlkId, pBlkId);
4312
0
  }
4313
4314
0
  if (QUERY_NODE_FUNCTION == nodeType(pExpr) && FUNCTION_TYPE_TBNAME == ((SFunctionNode*)pExpr)->funcType) {
4315
0
    int32_t code = getHashJoinNodeBlkId(pCxt, pExpr, leftBlkId, rightBlkId, pBlkId);
4316
0
    if (TSDB_CODE_PLAN_SLOT_NOT_FOUND == code) {
4317
0
      planError("%s", "Invalid hash join tbname key slot");
4318
0
    }
4319
0
    return code;
4320
0
  }
4321
4322
0
  SNodeList* pCols = NULL;
4323
0
  int32_t code = nodesCollectColumnsFromNode(pExpr, NULL, COLLECT_COL_TYPE_ALL, &pCols);
4324
0
  if (TSDB_CODE_SUCCESS != code) return code;
4325
0
  if (NULL == pCols || 0 == LIST_LENGTH(pCols)) {
4326
0
    nodesDestroyList(pCols);
4327
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
4328
0
  }
4329
0
  int64_t blockId = -1;
4330
0
  SNode* pCol = NULL;
4331
0
  FOREACH(pCol, pCols) {
4332
0
    if (QUERY_NODE_COLUMN != nodeType(pCol)) {
4333
0
      nodesDestroyList(pCols);
4334
0
      return TSDB_CODE_PLAN_NOT_SUPPORT_JOIN_COND;
4335
0
    }
4336
4337
0
    int64_t colBlockId = -1;
4338
0
    code = getHashJoinNodeBlkId(pCxt, pCol, leftBlkId, rightBlkId, &colBlockId);
4339
0
    if (TSDB_CODE_SUCCESS != code) {
4340
0
      nodesDestroyList(pCols);
4341
0
      return code;
4342
0
    }
4343
4344
0
    if (blockId < 0) {
4345
0
      blockId = colBlockId;
4346
0
    } else if (blockId != colBlockId) {
4347
0
      nodesDestroyList(pCols);
4348
0
      return TSDB_CODE_PLAN_NOT_SUPPORT_JOIN_COND;
4349
0
    }
4350
0
  }
4351
0
  nodesDestroyList(pCols);
4352
0
  if (blockId != leftBlkId && blockId != rightBlkId) {
4353
0
    return TSDB_CODE_PLAN_NOT_SUPPORT_JOIN_COND;
4354
0
  }
4355
0
  *pBlkId = blockId;
4356
0
  return TSDB_CODE_SUCCESS;
4357
0
}
4358
4359
0
static int32_t createHashJoinKeyCol(SPhysiPlanContext* pCxt, SNode* pExpr, int64_t inputBlockId, SNode** ppCol) {
4360
0
  if (QUERY_NODE_COLUMN == nodeType(pExpr)) {
4361
0
    return nodesCloneNode(pExpr, ppCol);
4362
0
  }
4363
4364
0
  if (QUERY_NODE_FUNCTION == nodeType(pExpr) && FUNCTION_TYPE_TBNAME == ((SFunctionNode*)pExpr)->funcType) {
4365
0
    char*   pName = NULL;
4366
0
    int32_t len = 0;
4367
0
    int32_t cap = 0;
4368
0
    int32_t code = getSlotKey(pExpr, NULL, &pName, &len, 0, &cap);
4369
0
    if (TSDB_CODE_SUCCESS != code) {
4370
0
      return code;
4371
0
    }
4372
4373
    // The caller has already resolved which JOIN input owns this expression.
4374
0
    SHashObj* pHash = taosArrayGetP(pCxt->pLocationHelper, inputBlockId);
4375
0
    SSlotIndex* pIndex = pHash != NULL ? taosHashGet(pHash, pName, len) : NULL;
4376
0
    taosMemoryFree(pName);
4377
4378
0
    if (NULL == pIndex) {
4379
0
      planError("%s", "Invalid hash join tbname key slot");
4380
0
      return TSDB_CODE_PLAN_SLOT_NOT_FOUND;
4381
0
    }
4382
0
    if (NULL == pIndex->pSlotIdsInfo || 0 == taosArrayGetSize(pIndex->pSlotIdsInfo)) {
4383
0
      planError("%s", "Invalid hash join tbname key slot info");
4384
0
      return TSDB_CODE_PLAN_SLOT_NOT_FOUND;
4385
0
    }
4386
4387
0
    SColumnNode* pCol = NULL;
4388
0
    code = nodesMakeNode(QUERY_NODE_COLUMN, (SNode**)&pCol);
4389
0
    if (TSDB_CODE_SUCCESS != code) {
4390
0
      return code;
4391
0
    }
4392
0
    pCol->dataBlockId = pIndex->dataBlockId;
4393
0
    pCol->slotId = ((SSlotIdInfo*)taosArrayGet(pIndex->pSlotIdsInfo, 0))->slotId;
4394
0
    pCol->colType = COLUMN_TYPE_COLUMN;
4395
0
    pCol->node.resType = ((SExprNode*)pExpr)->resType;
4396
0
    tstrncpy(pCol->colName, ((SExprNode*)pExpr)->aliasName, TSDB_COL_NAME_LEN);
4397
0
    tstrncpy(pCol->node.aliasName, ((SExprNode*)pExpr)->aliasName, TSDB_COL_NAME_LEN);
4398
0
    tstrncpy(pCol->node.userAlias, ((SExprNode*)pExpr)->aliasName, TSDB_COL_NAME_LEN);
4399
0
    *ppCol = (SNode*)pCol;
4400
0
    return TSDB_CODE_SUCCESS;
4401
0
  }
4402
4403
0
  planError("Invalid hash join key node type:%d", nodeType(pExpr));
4404
0
  return TSDB_CODE_PLAN_INTERNAL_ERROR;
4405
0
}
4406
4407
0
static int32_t normalizeHashJoinKeyTypes(SNode* pLeft, SNode* pRight) {
4408
0
  if (NULL == pLeft || NULL == pRight) {
4409
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
4410
0
  }
4411
4412
0
  SNode* pLeftExpr = QUERY_NODE_TARGET == nodeType(pLeft) ? ((STargetNode*)pLeft)->pExpr : pLeft;
4413
0
  SNode* pRightExpr = QUERY_NODE_TARGET == nodeType(pRight) ? ((STargetNode*)pRight)->pExpr : pRight;
4414
0
  if (!nodesIsExprNode(pLeftExpr) || !nodesIsExprNode(pRightExpr)) return TSDB_CODE_PLAN_INTERNAL_ERROR;
4415
0
  SDataType* pLeftType = &((SExprNode*)pLeftExpr)->resType;
4416
0
  SDataType* pRightType = &((SExprNode*)pRightExpr)->resType;
4417
0
  if (pLeftType->type == pRightType->type) {
4418
0
    return TSDB_CODE_SUCCESS;
4419
0
  }
4420
4421
0
  if (!IS_NUMERIC_TYPE(pLeftType->type) || !IS_NUMERIC_TYPE(pRightType->type)) {
4422
0
    return TSDB_CODE_PLAN_NOT_SUPPORT_JOIN_COND;
4423
0
  }
4424
4425
0
  int32_t commonType = vectorGetConvertType(pLeftType->type, pRightType->type);
4426
0
  if (commonType < TSDB_DATA_TYPE_NULL || commonType >= TSDB_DATA_TYPE_MAX) {
4427
0
    return TSDB_CODE_PLAN_NOT_SUPPORT_JOIN_COND;
4428
0
  }
4429
4430
0
  pLeftType->type = commonType;
4431
0
  pRightType->type = commonType;
4432
0
  pLeftType->bytes = tDataTypes[commonType].bytes;
4433
0
  pRightType->bytes = tDataTypes[commonType].bytes;
4434
0
  return TSDB_CODE_SUCCESS;
4435
0
}
4436
4437
static int32_t createHashJoinExprTarget(SPhysiPlanContext* pCxt, SNode* pExpr, SDataBlockDescNode* pDesc,
4438
0
                                        SNode** ppTarget) {
4439
0
  int32_t code = setNodeSlotId(pCxt, pDesc->dataBlockId, -1, pExpr, ppTarget);
4440
0
  if (TSDB_CODE_SUCCESS == code) {
4441
0
    code = addDataBlockSlot(pCxt, ppTarget, pDesc);
4442
0
  }
4443
0
  return code;
4444
0
}
4445
4446
static int32_t extractHashJoinOpCols(SPhysiPlanContext* pCxt, SDataBlockDescNode* pLeftDesc,
4447
0
                                     SDataBlockDescNode* pRightDesc, SNode* pEq, SHashJoinPhysiNode* pJoin) {
4448
0
  int32_t code = 0;
4449
0
  int64_t lBlkId = pLeftDesc->dataBlockId;
4450
0
  int64_t rBlkId = pRightDesc->dataBlockId;
4451
0
  if (QUERY_NODE_OPERATOR != nodeType(pEq)) {
4452
0
    planError("Invalid join equal node type:%d", nodeType(pEq));
4453
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
4454
0
  }
4455
4456
0
  SOperatorNode* pOp = (SOperatorNode*)pEq;
4457
0
  int64_t        leftExprBlkId = 0;
4458
0
  int64_t        rightExprBlkId = 0;
4459
4460
0
  switch (nodeType(pOp->pLeft)) {
4461
0
    case QUERY_NODE_COLUMN:
4462
0
      leftExprBlkId = ((SColumnNode*)pOp->pLeft)->dataBlockId;
4463
0
      break;
4464
0
    case QUERY_NODE_TARGET:
4465
0
      leftExprBlkId = ((STargetNode*)pOp->pLeft)->dataBlockId;
4466
0
      break;
4467
0
    case QUERY_NODE_VALUE:
4468
0
      if (NULL == pJoin->leftPrimExpr || QUERY_NODE_TARGET != nodeType(pJoin->leftPrimExpr)) {
4469
0
        return TSDB_CODE_PLAN_INTERNAL_ERROR;
4470
0
      }
4471
0
      leftExprBlkId = ((STargetNode*)pJoin->leftPrimExpr)->dataBlockId;
4472
0
      break;
4473
0
    case QUERY_NODE_FUNCTION: {
4474
0
      SFunctionNode* pFunc = (SFunctionNode*)pOp->pLeft;
4475
0
      if (FUNCTION_TYPE_TBNAME == pFunc->funcType) {
4476
0
        code = getHashJoinExprBlkId(pCxt, pOp->pLeft, lBlkId, rBlkId, &leftExprBlkId);
4477
0
        if (TSDB_CODE_SUCCESS != code) {
4478
0
          return code;
4479
0
        }
4480
0
        break;
4481
0
      }
4482
0
      if (FUNCTION_TYPE_TIMETRUNCATE != pFunc->funcType && FUNCTION_TYPE_TIMESTAMP_SCALE != pFunc->funcType) {
4483
0
        code = getHashJoinExprBlkId(pCxt, pOp->pLeft, lBlkId, rBlkId, &leftExprBlkId);
4484
0
        if (TSDB_CODE_SUCCESS != code) return code;
4485
0
        break;
4486
0
      }
4487
0
      code = getHashJoinExprBlkId(pCxt, pOp->pLeft, lBlkId, rBlkId, &leftExprBlkId);
4488
0
      if (TSDB_CODE_SUCCESS != code) return code;
4489
0
      break;
4490
0
    }
4491
0
    default:
4492
0
      code = getHashJoinExprBlkId(pCxt, pOp->pLeft, lBlkId, rBlkId, &leftExprBlkId);
4493
0
      if (TSDB_CODE_SUCCESS != code) return code;
4494
0
      break;
4495
0
  }
4496
4497
0
  switch (nodeType(pOp->pRight)) {
4498
0
    case QUERY_NODE_COLUMN:
4499
0
      rightExprBlkId = ((SColumnNode*)pOp->pRight)->dataBlockId;
4500
0
      break;
4501
0
    case QUERY_NODE_TARGET:
4502
0
      rightExprBlkId = ((STargetNode*)pOp->pRight)->dataBlockId;
4503
0
      break;
4504
0
    case QUERY_NODE_VALUE:
4505
0
      if (NULL == pJoin->rightPrimExpr || QUERY_NODE_TARGET != nodeType(pJoin->rightPrimExpr)) {
4506
0
        return TSDB_CODE_PLAN_INTERNAL_ERROR;
4507
0
      }
4508
0
      rightExprBlkId = ((STargetNode*)pJoin->rightPrimExpr)->dataBlockId;
4509
0
      break;
4510
0
    case QUERY_NODE_FUNCTION: {
4511
0
      SFunctionNode* pFunc = (SFunctionNode*)pOp->pRight;
4512
0
      if (FUNCTION_TYPE_TBNAME == pFunc->funcType) {
4513
0
        code = getHashJoinExprBlkId(pCxt, pOp->pRight, lBlkId, rBlkId, &rightExprBlkId);
4514
0
        if (TSDB_CODE_SUCCESS != code) {
4515
0
          return code;
4516
0
        }
4517
0
        break;
4518
0
      }
4519
0
      if (FUNCTION_TYPE_TIMETRUNCATE != pFunc->funcType && FUNCTION_TYPE_TIMESTAMP_SCALE != pFunc->funcType) {
4520
0
        code = getHashJoinExprBlkId(pCxt, pOp->pRight, lBlkId, rBlkId, &rightExprBlkId);
4521
0
        if (TSDB_CODE_SUCCESS != code) return code;
4522
0
        break;
4523
0
      }
4524
0
      code = getHashJoinExprBlkId(pCxt, pOp->pRight, lBlkId, rBlkId, &rightExprBlkId);
4525
0
      if (TSDB_CODE_SUCCESS != code) return code;
4526
0
      break;
4527
0
    }
4528
0
    default:
4529
0
      code = getHashJoinExprBlkId(pCxt, pOp->pRight, lBlkId, rBlkId, &rightExprBlkId);
4530
0
      if (TSDB_CODE_SUCCESS != code) return code;
4531
0
      break;
4532
0
  }
4533
4534
0
  bool validExprBlocks = (lBlkId == leftExprBlkId && rBlkId == rightExprBlkId) ||
4535
0
                         (rBlkId == leftExprBlkId && lBlkId == rightExprBlkId);
4536
0
  if (!validExprBlocks && QUERY_NODE_FUNCTION == nodeType(pOp->pLeft) &&
4537
0
      QUERY_NODE_FUNCTION == nodeType(pOp->pRight)) {
4538
0
    SFunctionNode* pLeftFunc = (SFunctionNode*)pOp->pLeft;
4539
0
    SFunctionNode* pRightFunc = (SFunctionNode*)pOp->pRight;
4540
0
    bool leftIsPrimFunc = FUNCTION_TYPE_TIMETRUNCATE == pLeftFunc->funcType ||
4541
0
                          FUNCTION_TYPE_TIMESTAMP_SCALE == pLeftFunc->funcType;
4542
0
    bool rightIsPrimFunc = FUNCTION_TYPE_TIMETRUNCATE == pRightFunc->funcType ||
4543
0
                           FUNCTION_TYPE_TIMESTAMP_SCALE == pRightFunc->funcType;
4544
0
    if (leftIsPrimFunc && rightIsPrimFunc) {
4545
0
      leftExprBlkId = lBlkId;
4546
0
      rightExprBlkId = rBlkId;
4547
0
    }
4548
0
  }
4549
4550
0
  SNode* pLeftExpr = NULL;
4551
0
  SNode* pRightExpr = NULL;
4552
4553
0
  if (lBlkId == leftExprBlkId && rBlkId == rightExprBlkId) {
4554
0
    if (QUERY_NODE_COLUMN == nodeType(pOp->pLeft)) {
4555
0
      code = nodesCloneNode(pOp->pLeft, &pLeftExpr);
4556
0
    } else if (QUERY_NODE_TARGET == nodeType(pOp->pLeft)) {
4557
0
      code = createHashJoinKeyColFromPrimExpr(pCxt, pOp->pLeft, &pLeftExpr);
4558
0
    } else if (QUERY_NODE_FUNCTION == nodeType(pOp->pLeft) &&
4559
0
               FUNCTION_TYPE_TBNAME == ((SFunctionNode*)pOp->pLeft)->funcType) {
4560
0
      code = createHashJoinKeyCol(pCxt, pOp->pLeft, leftExprBlkId, &pLeftExpr);
4561
0
    } else if (NULL != pJoin->leftPrimExpr && QUERY_NODE_FUNCTION == nodeType(pOp->pLeft) &&
4562
0
               (FUNCTION_TYPE_TIMETRUNCATE == ((SFunctionNode*)pOp->pLeft)->funcType ||
4563
0
                FUNCTION_TYPE_TIMESTAMP_SCALE == ((SFunctionNode*)pOp->pLeft)->funcType)) {
4564
0
      code = createHashJoinKeyColFromPrimExpr(pCxt, pJoin->leftPrimExpr, &pLeftExpr);
4565
0
    } else {
4566
0
      code = createHashJoinExprTarget(pCxt, pOp->pLeft, pLeftDesc, &pLeftExpr);
4567
0
    }
4568
0
    if (TSDB_CODE_SUCCESS == code) {
4569
0
      code = nodesListStrictAppend(pJoin->pOnLeft, pLeftExpr);
4570
0
    }
4571
0
    if (TSDB_CODE_SUCCESS == code) {
4572
0
      if (QUERY_NODE_COLUMN == nodeType(pOp->pRight)) {
4573
0
        code = nodesCloneNode(pOp->pRight, &pRightExpr);
4574
0
      } else if (QUERY_NODE_TARGET == nodeType(pOp->pRight)) {
4575
0
        code = createHashJoinKeyColFromPrimExpr(pCxt, pOp->pRight, &pRightExpr);
4576
0
      } else if (QUERY_NODE_FUNCTION == nodeType(pOp->pRight) &&
4577
0
                 FUNCTION_TYPE_TBNAME == ((SFunctionNode*)pOp->pRight)->funcType) {
4578
0
        code = createHashJoinKeyCol(pCxt, pOp->pRight, rightExprBlkId, &pRightExpr);
4579
0
      } else if (NULL != pJoin->rightPrimExpr && QUERY_NODE_FUNCTION == nodeType(pOp->pRight) &&
4580
0
                 (FUNCTION_TYPE_TIMETRUNCATE == ((SFunctionNode*)pOp->pRight)->funcType ||
4581
0
                  FUNCTION_TYPE_TIMESTAMP_SCALE == ((SFunctionNode*)pOp->pRight)->funcType)) {
4582
0
        code = createHashJoinKeyColFromPrimExpr(pCxt, pJoin->rightPrimExpr, &pRightExpr);
4583
0
      } else {
4584
0
        code = createHashJoinExprTarget(pCxt, pOp->pRight, pRightDesc, &pRightExpr);
4585
0
      }
4586
0
    }
4587
0
    if (TSDB_CODE_SUCCESS == code) {
4588
0
      code = nodesListStrictAppend(pJoin->pOnRight, pRightExpr);
4589
0
    }
4590
0
  } else if (rBlkId == leftExprBlkId && lBlkId == rightExprBlkId) {
4591
0
    if (QUERY_NODE_COLUMN == nodeType(pOp->pRight)) {
4592
0
      code = nodesCloneNode(pOp->pRight, &pRightExpr);
4593
0
    } else if (QUERY_NODE_TARGET == nodeType(pOp->pRight)) {
4594
0
      code = createHashJoinKeyColFromPrimExpr(pCxt, pOp->pRight, &pRightExpr);
4595
0
    } else if (QUERY_NODE_FUNCTION == nodeType(pOp->pRight) &&
4596
0
               FUNCTION_TYPE_TBNAME == ((SFunctionNode*)pOp->pRight)->funcType) {
4597
0
      code = createHashJoinKeyCol(pCxt, pOp->pRight, rightExprBlkId, &pRightExpr);
4598
0
    } else if (NULL != pJoin->leftPrimExpr && QUERY_NODE_FUNCTION == nodeType(pOp->pRight) &&
4599
0
               (FUNCTION_TYPE_TIMETRUNCATE == ((SFunctionNode*)pOp->pRight)->funcType ||
4600
0
                FUNCTION_TYPE_TIMESTAMP_SCALE == ((SFunctionNode*)pOp->pRight)->funcType)) {
4601
0
      code = createHashJoinKeyColFromPrimExpr(pCxt, pJoin->leftPrimExpr, &pRightExpr);
4602
0
    } else {
4603
0
      code = createHashJoinExprTarget(pCxt, pOp->pRight, pLeftDesc, &pRightExpr);
4604
0
    }
4605
0
    if (TSDB_CODE_SUCCESS == code) {
4606
0
      code = nodesListStrictAppend(pJoin->pOnLeft, pRightExpr);
4607
0
    }
4608
0
    if (TSDB_CODE_SUCCESS == code) {
4609
0
      if (QUERY_NODE_COLUMN == nodeType(pOp->pLeft)) {
4610
0
        code = nodesCloneNode(pOp->pLeft, &pLeftExpr);
4611
0
      } else if (QUERY_NODE_TARGET == nodeType(pOp->pLeft)) {
4612
0
        code = createHashJoinKeyColFromPrimExpr(pCxt, pOp->pLeft, &pLeftExpr);
4613
0
      } else if (QUERY_NODE_FUNCTION == nodeType(pOp->pLeft) &&
4614
0
                 FUNCTION_TYPE_TBNAME == ((SFunctionNode*)pOp->pLeft)->funcType) {
4615
0
        code = createHashJoinKeyCol(pCxt, pOp->pLeft, leftExprBlkId, &pLeftExpr);
4616
0
      } else if (NULL != pJoin->rightPrimExpr && QUERY_NODE_FUNCTION == nodeType(pOp->pLeft) &&
4617
0
                 (FUNCTION_TYPE_TIMETRUNCATE == ((SFunctionNode*)pOp->pLeft)->funcType ||
4618
0
                  FUNCTION_TYPE_TIMESTAMP_SCALE == ((SFunctionNode*)pOp->pLeft)->funcType)) {
4619
0
        code = createHashJoinKeyColFromPrimExpr(pCxt, pJoin->rightPrimExpr, &pLeftExpr);
4620
0
      } else {
4621
0
        code = createHashJoinExprTarget(pCxt, pOp->pLeft, pRightDesc, &pLeftExpr);
4622
0
      }
4623
0
    }
4624
0
    if (TSDB_CODE_SUCCESS == code) {
4625
0
      code = nodesListStrictAppend(pJoin->pOnRight, pLeftExpr);
4626
0
    }
4627
0
  } else {
4628
0
    planError("Invalid join equal cond, lbid:%" PRId64 ", rbid:%" PRId64 ", oplid:%" PRId64 ", oprid:%" PRId64,
4629
0
              lBlkId, rBlkId, leftExprBlkId, rightExprBlkId);
4630
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
4631
0
  }
4632
4633
0
  if (TSDB_CODE_SUCCESS == code) {
4634
0
    code = normalizeHashJoinKeyTypes(pLeftExpr, pRightExpr);
4635
0
  }
4636
0
  return code;
4637
0
}
4638
4639
static int32_t extractHashJoinOnCols(SPhysiPlanContext* pCxt, SDataBlockDescNode* pLeftDesc,
4640
0
                                     SDataBlockDescNode* pRightDesc, SNode* pEq, SHashJoinPhysiNode* pJoin) {
4641
0
  if (NULL == pEq) {
4642
0
    return TSDB_CODE_SUCCESS;
4643
0
  }
4644
4645
0
  int32_t code = TSDB_CODE_SUCCESS;
4646
0
  if (QUERY_NODE_OPERATOR == nodeType(pEq)) {
4647
0
    code = extractHashJoinOpCols(pCxt, pLeftDesc, pRightDesc, pEq, pJoin);
4648
0
  } else if (QUERY_NODE_LOGIC_CONDITION == nodeType(pEq)) {
4649
0
    SLogicConditionNode* pLogic = (SLogicConditionNode*)pEq;
4650
0
    SNode*               pNode = NULL;
4651
0
    FOREACH(pNode, pLogic->pParameterList) {
4652
0
      code = extractHashJoinOpCols(pCxt, pLeftDesc, pRightDesc, pNode, pJoin);
4653
0
      if (code) {
4654
0
        break;
4655
0
      }
4656
0
    }
4657
0
  } else {
4658
0
    planError("Invalid join equal node type:%d", nodeType(pEq));
4659
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
4660
0
  }
4661
4662
0
  return code;
4663
0
}
4664
4665
static int32_t createHashJoinColList(SPhysiPlanContext* pCxt, SDataBlockDescNode* pLeftDesc,
4666
                                     SDataBlockDescNode* pRightDesc, SNode* pEq1, SNode* pEq2, SNode* pEq3,
4667
0
                                     SHashJoinPhysiNode* pJoin) {
4668
0
  int32_t code = TSDB_CODE_SUCCESS;
4669
0
  pJoin->pOnLeft = NULL;
4670
0
  code = nodesMakeList(&pJoin->pOnLeft);
4671
0
  if (TSDB_CODE_SUCCESS != code) {
4672
0
    return code;
4673
0
  }
4674
0
  pJoin->pOnRight = NULL;
4675
0
  code = nodesMakeList(&pJoin->pOnRight);
4676
0
  if (TSDB_CODE_SUCCESS != code) {
4677
0
    return code;
4678
0
  }
4679
4680
0
  code = extractHashJoinOnCols(pCxt, pLeftDesc, pRightDesc, pEq1, pJoin);
4681
0
  if (TSDB_CODE_SUCCESS == code) {
4682
0
    code = extractHashJoinOnCols(pCxt, pLeftDesc, pRightDesc, pEq2, pJoin);
4683
0
  }
4684
0
  if (TSDB_CODE_SUCCESS == code) {
4685
0
    code = extractHashJoinOnCols(pCxt, pLeftDesc, pRightDesc, pEq3, pJoin);
4686
0
  }
4687
0
  if (TSDB_CODE_SUCCESS == code && pJoin->pOnLeft->length <= 0) {
4688
0
    planError("Invalid join equal column num: %d", pJoin->pOnLeft->length);
4689
0
    code = TSDB_CODE_PLAN_INTERNAL_ERROR;
4690
0
  }
4691
4692
0
  return code;
4693
0
}
4694
4695
0
static int32_t sortHashJoinTargets(int64_t lBlkId, int64_t rBlkId, SHashJoinPhysiNode* pJoin) {
4696
0
  SNode*     pNode = NULL;
4697
0
  SSHashObj* pHash = tSimpleHashInit(pJoin->pTargets->length, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY));
4698
0
  if (NULL == pHash) {
4699
0
    return TSDB_CODE_OUT_OF_MEMORY;
4700
0
  }
4701
0
  SNodeList* pNew = NULL;
4702
0
  int32_t    code = nodesMakeList(&pNew);
4703
4704
0
  if (TSDB_CODE_SUCCESS == code) {
4705
0
    FOREACH(pNode, pJoin->pTargets) {
4706
0
      SColumnNode* pCol = (SColumnNode*)pNode;
4707
0
      char*        pName = NULL;
4708
0
      int32_t      len = 0;
4709
0
      int32_t      cap = 0;
4710
0
      code = getSlotKey(pNode, NULL, &pName, &len, 0, &cap);
4711
0
      if (TSDB_CODE_SUCCESS == code) {
4712
0
        code = tSimpleHashPut(pHash, pName, len, &pCol, POINTER_BYTES);
4713
0
      }
4714
0
      taosMemoryFree(pName);
4715
0
      if (TSDB_CODE_SUCCESS != code) {
4716
0
        break;
4717
0
      }
4718
0
    }
4719
0
  }
4720
0
  if (TSDB_CODE_SUCCESS == code) {
4721
0
    nodesClearList(pJoin->pTargets);
4722
0
    pJoin->pTargets = pNew;
4723
4724
0
    FOREACH(pNode, pJoin->pOnLeft) {
4725
0
      char*        pName = NULL;
4726
0
      SColumnNode* pCol = (SColumnNode*)pNode;
4727
0
      int32_t      len = 0;
4728
0
      int32_t      cap = 0;
4729
0
      code = getSlotKey(pNode, NULL, &pName, &len, 0, &cap);
4730
0
      if (TSDB_CODE_SUCCESS == code) {
4731
0
        SNode** p = tSimpleHashGet(pHash, pName, len);
4732
0
        if (p) {
4733
0
          code = nodesListStrictAppend(pJoin->pTargets, *p);
4734
0
          if (TSDB_CODE_SUCCESS == code) {
4735
0
            code = tSimpleHashRemove(pHash, pName, len);
4736
0
          }
4737
0
        }
4738
0
      }
4739
0
      taosMemoryFree(pName);
4740
0
      if (TSDB_CODE_SUCCESS != code) {
4741
0
        break;
4742
0
      }
4743
0
    }
4744
0
  }
4745
0
  if (TSDB_CODE_SUCCESS == code) {
4746
0
    FOREACH(pNode, pJoin->pOnRight) {
4747
0
      char*        pName = NULL;
4748
0
      SColumnNode* pCol = (SColumnNode*)pNode;
4749
0
      int32_t      len = 0;
4750
0
      int32_t cap = 0;
4751
0
      code = getSlotKey(pNode, NULL, &pName, &len, 0, &cap);
4752
0
      if (TSDB_CODE_SUCCESS == code) {
4753
0
        SNode** p = tSimpleHashGet(pHash, pName, len);
4754
0
        if (p) {
4755
0
          code = nodesListStrictAppend(pJoin->pTargets, *p);
4756
0
          if (TSDB_CODE_SUCCESS == code) {
4757
0
            code = tSimpleHashRemove(pHash, pName, len);
4758
0
          }
4759
0
        }
4760
0
      }
4761
0
      taosMemoryFree(pName);
4762
0
      if (TSDB_CODE_SUCCESS != code) {
4763
0
        break;
4764
0
      }
4765
0
    }
4766
0
  }
4767
0
  if (TSDB_CODE_SUCCESS == code) {
4768
0
    if (tSimpleHashGetSize(pHash) > 0) {
4769
0
      SNode** p = NULL;
4770
0
      int32_t iter = 0;
4771
0
      while (1) {
4772
0
        p = tSimpleHashIterate(pHash, p, &iter);
4773
0
        if (p == NULL) {
4774
0
          break;
4775
0
        }
4776
4777
0
        code = nodesListStrictAppend(pJoin->pTargets, *p);
4778
0
        if (TSDB_CODE_SUCCESS != code) {
4779
0
          break;
4780
0
        }
4781
0
      }
4782
0
    }
4783
0
  }
4784
4785
0
  tSimpleHashCleanup(pHash);
4786
4787
0
  return code;
4788
0
}
4789
4790
static int32_t setHashJoinPrimColEqCond(SNode* pEqCond, int64_t leftBlkId, int64_t rightBlkId,
4791
0
                                        SHashJoinPhysiNode* pJoin) {
4792
0
  int32_t code = 0;
4793
0
  if (QUERY_NODE_OPERATOR == nodeType(pEqCond)) {
4794
0
    SOperatorNode* pOp = (SOperatorNode*)pEqCond;
4795
0
    if (pOp->opType != OP_TYPE_EQUAL) {
4796
0
      planError("invalid primary cond opType, opType:%d", pOp->opType);
4797
0
      return TSDB_CODE_PLAN_INTERNAL_ERROR;
4798
0
    }
4799
4800
    // Projection-backed subqueries can leave one direct column operand with
4801
    // an unset dataBlockId even after slot binding.  When the other operand
4802
    // identifies its side, the missing side is unambiguous; normalize it
4803
    // before assigning the hash-join primary slots.
4804
0
    SColumnNode* pLeftCol = NULL;
4805
0
    SColumnNode* pRightCol = NULL;
4806
0
    if (QUERY_NODE_COLUMN == nodeType(pOp->pLeft)) {
4807
0
      pLeftCol = (SColumnNode*)pOp->pLeft;
4808
0
    } else if (QUERY_NODE_FUNCTION == nodeType(pOp->pLeft)) {
4809
0
      SFunctionNode* pFunc = (SFunctionNode*)pOp->pLeft;
4810
0
      SNode* pParam = nodesListGetNode(pFunc->pParameterList, 0);
4811
0
      if (NULL != pParam && QUERY_NODE_COLUMN == nodeType(pParam)) {
4812
0
        pLeftCol = (SColumnNode*)pParam;
4813
0
      }
4814
0
    }
4815
0
    if (QUERY_NODE_COLUMN == nodeType(pOp->pRight)) {
4816
0
      pRightCol = (SColumnNode*)pOp->pRight;
4817
0
    } else if (QUERY_NODE_FUNCTION == nodeType(pOp->pRight)) {
4818
0
      SFunctionNode* pFunc = (SFunctionNode*)pOp->pRight;
4819
0
      SNode* pParam = nodesListGetNode(pFunc->pParameterList, 0);
4820
0
      if (NULL != pParam && QUERY_NODE_COLUMN == nodeType(pParam)) {
4821
0
        pRightCol = (SColumnNode*)pParam;
4822
0
      }
4823
0
    }
4824
0
    if (NULL != pLeftCol && NULL != pRightCol) {
4825
0
      bool leftKnown = (pLeftCol->dataBlockId == leftBlkId || pLeftCol->dataBlockId == rightBlkId);
4826
0
      bool rightKnown = (pRightCol->dataBlockId == leftBlkId || pRightCol->dataBlockId == rightBlkId);
4827
0
      if (leftKnown && rightKnown && pLeftCol->dataBlockId == pRightCol->dataBlockId) {
4828
0
        pLeftCol->dataBlockId = leftBlkId;
4829
0
        pRightCol->dataBlockId = rightBlkId;
4830
0
      } else if (!leftKnown && rightKnown) {
4831
0
        pLeftCol->dataBlockId = (pRightCol->dataBlockId == rightBlkId) ? leftBlkId : rightBlkId;
4832
0
      } else if (leftKnown && !rightKnown) {
4833
0
        pRightCol->dataBlockId = (pLeftCol->dataBlockId == leftBlkId) ? rightBlkId : leftBlkId;
4834
0
      } else if (!leftKnown && !rightKnown) {
4835
0
        pLeftCol->dataBlockId = leftBlkId;
4836
0
        pRightCol->dataBlockId = rightBlkId;
4837
0
      }
4838
0
    }
4839
4840
0
    switch (nodeType(pOp->pLeft)) {
4841
0
      case QUERY_NODE_COLUMN: {
4842
0
        SColumnNode* pCol = (SColumnNode*)pOp->pLeft;
4843
0
        if (leftBlkId == pCol->dataBlockId) {
4844
0
          pJoin->leftPrimSlotId = pCol->slotId;
4845
0
        } else if (rightBlkId == pCol->dataBlockId) {
4846
0
          pJoin->rightPrimSlotId = pCol->slotId;
4847
0
        } else {
4848
0
          planError("invalid primary key col equal cond, leftBlockId:%" PRId64, pCol->dataBlockId);
4849
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
4850
0
        }
4851
0
        break;
4852
0
      }
4853
0
      case QUERY_NODE_VALUE: {
4854
0
        pJoin->leftPrimExpr = NULL;
4855
0
        code = nodesCloneNode(pOp->pLeft, &pJoin->leftPrimExpr);
4856
0
        break;
4857
0
      }
4858
0
      case QUERY_NODE_FUNCTION: {
4859
0
        SFunctionNode* pFunc = (SFunctionNode*)pOp->pLeft;
4860
0
        if (FUNCTION_TYPE_TIMETRUNCATE != pFunc->funcType &&
4861
0
            FUNCTION_TYPE_TIMESTAMP_SCALE != pFunc->funcType) {
4862
0
          planError("invalid primary cond left function type, leftFuncType:%d", pFunc->funcType);
4863
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
4864
0
        }
4865
0
        SNode* pParam = nodesListGetNode(pFunc->pParameterList, 0);
4866
0
        if (NULL == pParam || QUERY_NODE_COLUMN != nodeType(pParam)) {
4867
0
          planError("invalid primary cond left timetruncate param");
4868
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
4869
0
        }
4870
0
        SColumnNode* pCol = (SColumnNode*)pParam;
4871
0
        if (leftBlkId == pCol->dataBlockId) {
4872
0
          pJoin->leftPrimSlotId = pCol->slotId;
4873
0
          pJoin->leftPrimExpr = NULL;
4874
0
          code = nodesCloneNode((SNode*)pFunc, &pJoin->leftPrimExpr);
4875
0
        } else if (rightBlkId == pCol->dataBlockId) {
4876
0
          pJoin->rightPrimSlotId = pCol->slotId;
4877
0
          pJoin->rightPrimExpr = NULL;
4878
0
          code = nodesCloneNode((SNode*)pFunc, &pJoin->rightPrimExpr);
4879
0
        } else {
4880
0
          planError("invalid primary key col equal cond, leftBlockId:%" PRId64, pCol->dataBlockId);
4881
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
4882
0
        }
4883
0
        break;
4884
0
      }
4885
0
      default:
4886
0
        planError("invalid primary cond left node type, leftNodeType:%d", nodeType(pOp->pLeft));
4887
0
        return TSDB_CODE_PLAN_INTERNAL_ERROR;
4888
0
    }
4889
0
    if (TSDB_CODE_SUCCESS != code) {
4890
0
      return code;
4891
0
    }
4892
0
    switch (nodeType(pOp->pRight)) {
4893
0
      case QUERY_NODE_COLUMN: {
4894
0
        SColumnNode* pCol = (SColumnNode*)pOp->pRight;
4895
0
        if (leftBlkId == pCol->dataBlockId) {
4896
0
          pJoin->leftPrimSlotId = pCol->slotId;
4897
0
        } else if (rightBlkId == pCol->dataBlockId) {
4898
0
          pJoin->rightPrimSlotId = pCol->slotId;
4899
0
        } else {
4900
0
          planError("invalid primary key col equal cond, rightBlockId:%" PRId64, pCol->dataBlockId);
4901
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
4902
0
        }
4903
0
        break;
4904
0
      }
4905
0
      case QUERY_NODE_VALUE: {
4906
0
        pJoin->rightPrimExpr = NULL;
4907
0
        code = nodesCloneNode(pOp->pRight, &pJoin->rightPrimExpr);
4908
0
        break;
4909
0
      }
4910
0
      case QUERY_NODE_FUNCTION: {
4911
0
        SFunctionNode* pFunc = (SFunctionNode*)pOp->pRight;
4912
0
        if (FUNCTION_TYPE_TIMETRUNCATE != pFunc->funcType &&
4913
0
            FUNCTION_TYPE_TIMESTAMP_SCALE != pFunc->funcType) {
4914
0
          planError("invalid primary cond right function type, rightFuncType:%d", pFunc->funcType);
4915
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
4916
0
        }
4917
0
        SNode* pParam = nodesListGetNode(pFunc->pParameterList, 0);
4918
0
        if (NULL == pParam || QUERY_NODE_COLUMN != nodeType(pParam)) {
4919
0
          planError("invalid primary cond right timetruncate param");
4920
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
4921
0
        }
4922
0
        SColumnNode* pCol = (SColumnNode*)pParam;
4923
0
        if (leftBlkId == pCol->dataBlockId) {
4924
0
          pJoin->leftPrimSlotId = pCol->slotId;
4925
0
          pJoin->leftPrimExpr = NULL;
4926
0
          code = nodesCloneNode((SNode*)pFunc, &pJoin->leftPrimExpr);
4927
0
        } else if (rightBlkId == pCol->dataBlockId) {
4928
0
          pJoin->rightPrimSlotId = pCol->slotId;
4929
0
          pJoin->rightPrimExpr = NULL;
4930
0
          code = nodesCloneNode((SNode*)pFunc, &pJoin->rightPrimExpr);
4931
0
        } else {
4932
0
          planError("invalid primary key col equal cond, rightBlockId:%" PRId64, pCol->dataBlockId);
4933
0
          return TSDB_CODE_PLAN_INTERNAL_ERROR;
4934
0
        }
4935
0
        break;
4936
0
      }
4937
0
      default:
4938
0
        planError("invalid primary cond right node type, rightNodeType:%d", nodeType(pOp->pRight));
4939
0
        return TSDB_CODE_PLAN_INTERNAL_ERROR;
4940
0
    }
4941
0
  } else {
4942
0
    planError("invalid primary key col equal cond, type:%d", nodeType(pEqCond));
4943
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
4944
0
  }
4945
4946
0
  return code;
4947
0
}
4948
4949
0
static bool getTbnameFuncTableAlias(SFunctionNode* pFunc, char* pAlias, int32_t aliasLen) {
4950
0
  if (FUNCTION_TYPE_TBNAME != pFunc->funcType) {
4951
0
    return false;
4952
0
  }
4953
4954
0
  SNode* pParam = nodesListGetNode(pFunc->pParameterList, 0);
4955
0
  if (NULL != pParam && QUERY_NODE_VALUE == nodeType(pParam)) {
4956
0
    SValueNode* pVal = (SValueNode*)pParam;
4957
0
    tstrncpy(pAlias, pVal->literal, aliasLen);
4958
0
    return pAlias[0] != '\0';
4959
0
  }
4960
4961
0
  char* pSuffix = NULL;
4962
0
  if (NULL != pFunc->node.userAlias) {
4963
0
    pSuffix = strstr(pFunc->node.userAlias, ".tbname");
4964
0
  }
4965
0
  if (NULL != pSuffix && pSuffix != pFunc->node.userAlias) {
4966
0
    int32_t len = TMIN((int32_t)(pSuffix - pFunc->node.userAlias), aliasLen - 1);
4967
0
    memcpy(pAlias, pFunc->node.userAlias, len);
4968
0
    pAlias[len] = '\0';
4969
0
    return true;
4970
0
  }
4971
4972
0
  return false;
4973
0
}
4974
4975
0
static bool logicNodeHasTableAlias(SLogicNode* pLogic, const char* pAlias) {
4976
0
  if (NULL == pLogic || NULL == pLogic->pTargets || NULL == pAlias || '\0' == pAlias[0]) {
4977
0
    return false;
4978
0
  }
4979
4980
0
  SNode* pNode = NULL;
4981
0
  FOREACH(pNode, pLogic->pTargets) {
4982
0
    if (QUERY_NODE_COLUMN == nodeType(pNode) && 0 == strcmp(((SColumnNode*)pNode)->tableAlias, pAlias)) {
4983
0
      return true;
4984
0
    }
4985
0
    if (QUERY_NODE_FUNCTION == nodeType(pNode) && FUNCTION_TYPE_TBNAME == ((SFunctionNode*)pNode)->funcType) {
4986
0
      char funcAlias[TSDB_TABLE_NAME_LEN] = {0};
4987
0
      if (getTbnameFuncTableAlias((SFunctionNode*)pNode, funcAlias, sizeof(funcAlias)) &&
4988
0
          0 == strcmp(funcAlias, pAlias)) {
4989
0
        return true;
4990
0
      }
4991
0
    }
4992
0
  }
4993
4994
0
  return false;
4995
0
}
4996
4997
static int32_t ensureHashJoinTbnameKeySlot(SPhysiPlanContext* pCxt, SFunctionNode* pFunc,
4998
                                           SJoinLogicNode* pJoinLogicNode,
4999
                                           SDataBlockDescNode* pLeftDesc,
5000
0
                                           SDataBlockDescNode* pRightDesc) {
5001
0
  char alias[TSDB_TABLE_NAME_LEN] = {0};
5002
0
  if (!getTbnameFuncTableAlias(pFunc, alias, sizeof(alias))) {
5003
0
    return TSDB_CODE_SUCCESS;
5004
0
  }
5005
5006
0
  SLogicNode*          pLeftLogic = (SLogicNode*)nodesListGetNode(pJoinLogicNode->node.pChildren, 0);
5007
0
  SLogicNode*          pRightLogic = (SLogicNode*)nodesListGetNode(pJoinLogicNode->node.pChildren, 1);
5008
0
  SDataBlockDescNode*  pDesc = NULL;
5009
0
  if (logicNodeHasTableAlias(pLeftLogic, alias)) {
5010
0
    pDesc = pLeftDesc;
5011
0
  } else if (logicNodeHasTableAlias(pRightLogic, alias)) {
5012
0
    pDesc = pRightDesc;
5013
0
  } else {
5014
0
    planError("Invalid hash join tbname key alias:%s", alias);
5015
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
5016
0
  }
5017
5018
0
  SNode*  pNode = NULL;
5019
0
  int32_t code = nodesCloneNode((SNode*)pFunc, &pNode);
5020
0
  if (TSDB_CODE_SUCCESS == code) {
5021
0
    code = addDataBlockSlot(pCxt, &pNode, pDesc);
5022
0
  }
5023
0
  nodesDestroyNode(pNode);
5024
0
  return code;
5025
0
}
5026
5027
static int32_t ensureHashJoinTbnameKeySlots(SPhysiPlanContext* pCxt, SNode* pEqCond,
5028
                                            SJoinLogicNode* pJoinLogicNode,
5029
                                            SDataBlockDescNode* pLeftDesc,
5030
0
                                            SDataBlockDescNode* pRightDesc) {
5031
0
  if (NULL == pEqCond) {
5032
0
    return TSDB_CODE_SUCCESS;
5033
0
  }
5034
5035
0
  int32_t code = TSDB_CODE_SUCCESS;
5036
0
  if (QUERY_NODE_OPERATOR == nodeType(pEqCond)) {
5037
0
    SOperatorNode* pOp = (SOperatorNode*)pEqCond;
5038
0
    if (QUERY_NODE_FUNCTION == nodeType(pOp->pLeft) &&
5039
0
        FUNCTION_TYPE_TBNAME == ((SFunctionNode*)pOp->pLeft)->funcType) {
5040
0
      code = ensureHashJoinTbnameKeySlot(pCxt, (SFunctionNode*)pOp->pLeft, pJoinLogicNode, pLeftDesc, pRightDesc);
5041
0
    }
5042
0
    if (TSDB_CODE_SUCCESS == code && QUERY_NODE_FUNCTION == nodeType(pOp->pRight) &&
5043
0
        FUNCTION_TYPE_TBNAME == ((SFunctionNode*)pOp->pRight)->funcType) {
5044
0
      code = ensureHashJoinTbnameKeySlot(pCxt, (SFunctionNode*)pOp->pRight, pJoinLogicNode, pLeftDesc, pRightDesc);
5045
0
    }
5046
0
  } else if (QUERY_NODE_LOGIC_CONDITION == nodeType(pEqCond)) {
5047
0
    SNode* pNode = NULL;
5048
0
    FOREACH(pNode, ((SLogicConditionNode*)pEqCond)->pParameterList) {
5049
0
      code = ensureHashJoinTbnameKeySlots(pCxt, pNode, pJoinLogicNode, pLeftDesc, pRightDesc);
5050
0
      if (TSDB_CODE_SUCCESS != code) {
5051
0
        break;
5052
0
      }
5053
0
    }
5054
0
  }
5055
5056
0
  return code;
5057
0
}
5058
5059
static int32_t createHashJoinPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren, SJoinLogicNode* pJoinLogicNode,
5060
0
                                       SPhysiNode** pPhyNode) {
5061
0
  SHashJoinPhysiNode* pJoin =
5062
0
      (SHashJoinPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pJoinLogicNode, QUERY_NODE_PHYSICAL_PLAN_HASH_JOIN);
5063
0
  if (NULL == pJoin) {
5064
0
    return terrno;
5065
0
  }
5066
5067
0
  SDataBlockDescNode* pLeftDesc = ((SPhysiNode*)nodesListGetNode(pChildren, 0))->pOutputDataBlockDesc;
5068
0
  SDataBlockDescNode* pRightDesc = ((SPhysiNode*)nodesListGetNode(pChildren, 1))->pOutputDataBlockDesc;
5069
0
  int32_t             code = TSDB_CODE_SUCCESS;
5070
5071
0
  pJoin->joinType = pJoinLogicNode->joinType;
5072
0
  pJoin->subType = pJoinLogicNode->subType;
5073
0
  pJoin->multiGroupJoin = pJoinLogicNode->multiGroupJoin;
5074
0
  pJoin->pWindowOffset = NULL;
5075
0
  code = nodesCloneNode(pJoinLogicNode->pWindowOffset, &pJoin->pWindowOffset);
5076
0
  if (TSDB_CODE_SUCCESS != code) {
5077
0
    nodesDestroyNode((SNode*)pJoin);
5078
0
    return code;
5079
0
  }
5080
0
  pJoin->pJLimit = NULL;
5081
0
  code = nodesCloneNode(pJoinLogicNode->pJLimit, &pJoin->pJLimit);
5082
0
  if (TSDB_CODE_SUCCESS != code) {
5083
0
    nodesDestroyNode((SNode*)pJoin);
5084
0
    return code;
5085
0
  }
5086
0
  pJoin->node.inputTsOrder = pJoinLogicNode->node.inputTsOrder;
5087
0
  pJoin->timeRangeTarget = pJoinLogicNode->timeRangeTarget;
5088
0
  pJoin->timeRange.skey = pJoinLogicNode->timeRange.skey;
5089
0
  pJoin->timeRange.ekey = pJoinLogicNode->timeRange.ekey;
5090
5091
0
  if (NULL != pJoinLogicNode->pPrimKeyEqCond) {
5092
0
    code = setJoinNodeSlotId(pCxt, pJoinLogicNode, pLeftDesc->dataBlockId, pRightDesc->dataBlockId,
5093
0
                             pJoinLogicNode->pPrimKeyEqCond, &pJoin->pPrimKeyCond);
5094
0
    if (TSDB_CODE_SUCCESS == code) {
5095
0
      code = setHashJoinPrimColEqCond(pJoin->pPrimKeyCond, pLeftDesc->dataBlockId, pRightDesc->dataBlockId, pJoin);
5096
0
    }
5097
0
    if (TSDB_CODE_SUCCESS == code && NULL != pJoin->leftPrimExpr) {
5098
0
      code = addDataBlockSlot(pCxt, &pJoin->leftPrimExpr, pLeftDesc);
5099
0
    }
5100
0
    if (TSDB_CODE_SUCCESS == code && NULL != pJoin->rightPrimExpr) {
5101
0
      code = addDataBlockSlot(pCxt, &pJoin->rightPrimExpr, pRightDesc);
5102
0
    }
5103
0
  }
5104
0
  if (TSDB_CODE_SUCCESS == code) {
5105
0
    code = ensureHashJoinTbnameKeySlots(pCxt, pJoinLogicNode->pColEqCond, pJoinLogicNode, pLeftDesc, pRightDesc);
5106
0
  }
5107
0
  if (TSDB_CODE_SUCCESS == code) {
5108
0
    code = ensureHashJoinTbnameKeySlots(pCxt, pJoinLogicNode->pTagEqCond, pJoinLogicNode, pLeftDesc, pRightDesc);
5109
0
  }
5110
0
  if (TSDB_CODE_SUCCESS == code) {
5111
0
    code = setJoinNodeSlotId(pCxt, pJoinLogicNode, pLeftDesc->dataBlockId, pRightDesc->dataBlockId,
5112
0
                             pJoinLogicNode->pColEqCond, &pJoin->pColEqCond);
5113
0
  }
5114
0
  if (TSDB_CODE_SUCCESS == code) {
5115
0
    code = setJoinNodeSlotId(pCxt, pJoinLogicNode, pLeftDesc->dataBlockId, pRightDesc->dataBlockId,
5116
0
                             pJoinLogicNode->pTagEqCond, &pJoin->pTagEqCond);
5117
0
  }
5118
0
  if (TSDB_CODE_SUCCESS == code) {
5119
0
    code = setNodeSlotId(pCxt, pLeftDesc->dataBlockId, -1, pJoinLogicNode->pLeftOnCond, &pJoin->pLeftOnCond);
5120
0
  }
5121
0
  if (TSDB_CODE_SUCCESS == code) {
5122
0
    code = setNodeSlotId(pCxt, -1, pRightDesc->dataBlockId, pJoinLogicNode->pRightOnCond, &pJoin->pRightOnCond);
5123
0
  }
5124
0
  if (TSDB_CODE_SUCCESS == code && ((NULL != pJoinLogicNode->pColOnCond) || (NULL != pJoinLogicNode->pTagOnCond))) {
5125
0
    code = mergeJoinConds(&pJoinLogicNode->pColOnCond, &pJoinLogicNode->pTagOnCond);
5126
0
  }
5127
0
  SNode* pOnCond = (NULL != pJoinLogicNode->pColOnCond) ? pJoinLogicNode->pColOnCond : pJoinLogicNode->pTagOnCond;
5128
0
  if (TSDB_CODE_SUCCESS == code) {
5129
    // Output targets retain outer aliases (a/b); use them only to disambiguate
5130
    // same-name slots across the two child descriptors.
5131
0
    code = setJoinTargetListSlotId(pCxt, pJoinLogicNode, pLeftDesc->dataBlockId, pRightDesc->dataBlockId,
5132
0
                                   pJoinLogicNode->node.pTargets, &pJoin->pTargets);
5133
0
  }
5134
0
  if (TSDB_CODE_SUCCESS == code) {
5135
0
    code = createHashJoinColList(pCxt, pLeftDesc, pRightDesc, pJoin->pPrimKeyCond, pJoin->pColEqCond,
5136
0
                                 pJoin->pTagEqCond, pJoin);
5137
0
  }
5138
0
  if (TSDB_CODE_SUCCESS == code) {
5139
0
    code = sortHashJoinTargets(pLeftDesc->dataBlockId, pRightDesc->dataBlockId, pJoin);
5140
0
  }
5141
0
  if (TSDB_CODE_SUCCESS == code) {
5142
0
    code = fqAppendJoinConditionTargets(pCxt, pJoinLogicNode, pLeftDesc->dataBlockId, pRightDesc->dataBlockId,
5143
0
                                        pJoin->pTargets);
5144
0
  }
5145
0
  if (TSDB_CODE_SUCCESS == code) {
5146
0
    code = addDataBlockSlots(pCxt, pJoin->pTargets, pJoin->node.pOutputDataBlockDesc);
5147
0
  }
5148
0
  if (TSDB_CODE_SUCCESS == code && NULL != pOnCond) {
5149
0
    code = setNodeSlotId(pCxt, pJoin->node.pOutputDataBlockDesc->dataBlockId, -1, pOnCond, &pJoin->pFullOnCond);
5150
0
    planDebug("FQ-joinSlots: hashJoin bind fullOnCond code=0x%x outputBlockId=%" PRId64 " cond=%p bound=%p",
5151
0
              code, pJoin->node.pOutputDataBlockDesc->dataBlockId, pOnCond, pJoin->pFullOnCond);
5152
0
  }
5153
0
  if (TSDB_CODE_SUCCESS == code) {
5154
0
    code = setConditionsSlotId(pCxt, (const SLogicNode*)pJoinLogicNode, (SPhysiNode*)pJoin);
5155
0
  }
5156
5157
0
  if (TSDB_CODE_SUCCESS == code) {
5158
0
    *pPhyNode = (SPhysiNode*)pJoin;
5159
0
  } else {
5160
0
    nodesDestroyNode((SNode*)pJoin);
5161
0
  }
5162
5163
0
  return code;
5164
0
}
5165
5166
static int32_t createThetaJoinPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
5167
0
                                        SJoinLogicNode* pJoinLogicNode, SPhysiNode** pPhyNode) {
5168
0
  SThetaJoinPhysiNode* pJoin =
5169
0
      (SThetaJoinPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pJoinLogicNode, QUERY_NODE_PHYSICAL_PLAN_THETA_JOIN);
5170
0
  if (NULL == pJoin) return terrno;
5171
5172
0
  SDataBlockDescNode* pLeftDesc = ((SPhysiNode*)nodesListGetNode(pChildren, 0))->pOutputDataBlockDesc;
5173
0
  SDataBlockDescNode* pRightDesc = ((SPhysiNode*)nodesListGetNode(pChildren, 1))->pOutputDataBlockDesc;
5174
0
  int32_t code = TSDB_CODE_SUCCESS;
5175
0
  pJoin->joinType = pJoinLogicNode->joinType;
5176
0
  pJoin->subType = pJoinLogicNode->subType;
5177
0
  pJoin->leftPrimSlotId = (int32_t)pLeftDesc->dataBlockId;
5178
0
  pJoin->rightPrimSlotId = (int32_t)pRightDesc->dataBlockId;
5179
0
  if (pJoinLogicNode->isCrossJoin) {
5180
0
    code = addCrossJoinChildAliasKeys(
5181
0
        pCxt, (SLogicNode*)nodesListGetNode(pJoinLogicNode->node.pChildren, 0), pLeftDesc);
5182
0
    if (TSDB_CODE_SUCCESS == code) {
5183
0
      code = addCrossJoinChildAliasKeys(
5184
0
          pCxt, (SLogicNode*)nodesListGetNode(pJoinLogicNode->node.pChildren, 1), pRightDesc);
5185
0
    }
5186
0
    if (TSDB_CODE_SUCCESS == code) {
5187
0
      code = setJoinTargetListSlotId(pCxt, pJoinLogicNode, pLeftDesc->dataBlockId, pRightDesc->dataBlockId,
5188
0
                                     pJoinLogicNode->node.pTargets, &pJoin->pTargets);
5189
0
    }
5190
0
  } else {
5191
0
    code = setListSlotId(pCxt, pLeftDesc->dataBlockId, pRightDesc->dataBlockId, pJoinLogicNode->node.pTargets,
5192
0
                         &pJoin->pTargets);
5193
0
  }
5194
0
  if (TSDB_CODE_SUCCESS == code) {
5195
0
    code = fqAppendJoinConditionTargets(pCxt, pJoinLogicNode, pLeftDesc->dataBlockId, pRightDesc->dataBlockId,
5196
0
                                        pJoin->pTargets);
5197
0
  }
5198
0
  if (TSDB_CODE_SUCCESS == code) code = addDataBlockSlots(pCxt, pJoin->pTargets, pJoin->node.pOutputDataBlockDesc);
5199
0
  if (TSDB_CODE_SUCCESS == code) {
5200
0
    code = setNodeSlotId(pCxt, pJoin->node.pOutputDataBlockDesc->dataBlockId, -1, pJoinLogicNode->pFullOnCond,
5201
0
                         &pJoin->pFullOnCond);
5202
0
  }
5203
0
  if (TSDB_CODE_SUCCESS == code) code = setConditionsSlotId(pCxt, (SLogicNode*)pJoinLogicNode, (SPhysiNode*)pJoin);
5204
0
  if (TSDB_CODE_SUCCESS == code) {
5205
0
    *pPhyNode = (SPhysiNode*)pJoin;
5206
0
  } else {
5207
0
    nodesDestroyNode((SNode*)pJoin);
5208
0
  }
5209
0
  return code;
5210
0
}
5211
5212
static int32_t createJoinPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren, SJoinLogicNode* pJoinLogicNode,
5213
0
                                   SPhysiNode** pPhyNode) {
5214
0
  if (pJoinLogicNode->multiGroupJoin) {
5215
0
    if (JOIN_ALGO_HASH != pJoinLogicNode->joinAlgo) {
5216
0
      planError("multi-group JOIN requires hash algorithm, actual:%d", pJoinLogicNode->joinAlgo);
5217
0
      return TSDB_CODE_PLAN_INTERNAL_ERROR;
5218
0
    }
5219
0
    int32_t code = createHashJoinPhysiNode(pCxt, pChildren, pJoinLogicNode, pPhyNode);
5220
0
    if (TSDB_CODE_SUCCESS == code && (QUERY_NODE_PHYSICAL_PLAN_HASH_JOIN != nodeType(*pPhyNode) ||
5221
0
                                      !((SHashJoinPhysiNode*)*pPhyNode)->multiGroupJoin)) {
5222
0
      planError("%s", "marked logical JOIN did not produce a marked physical hash JOIN");
5223
0
      nodesDestroyNode((SNode*)*pPhyNode);
5224
0
      *pPhyNode = NULL;
5225
0
      return TSDB_CODE_PLAN_INTERNAL_ERROR;
5226
0
    }
5227
0
    return code;
5228
0
  }
5229
5230
0
  switch (pJoinLogicNode->joinAlgo) {
5231
0
    case JOIN_ALGO_MERGE:
5232
0
      return createMergeJoinPhysiNode(pCxt, pChildren, pJoinLogicNode, pPhyNode);
5233
0
    case JOIN_ALGO_HASH:
5234
0
      return createHashJoinPhysiNode(pCxt, pChildren, pJoinLogicNode, pPhyNode);
5235
0
    case JOIN_ALGO_THETA:
5236
0
      return createThetaJoinPhysiNode(pCxt, pChildren, pJoinLogicNode, pPhyNode);
5237
0
    default:
5238
0
      planError("Invalid join algorithm:%d", pJoinLogicNode->joinAlgo);
5239
0
      break;
5240
0
  }
5241
5242
0
  return TSDB_CODE_FAILED;
5243
0
}
5244
5245
0
static int32_t createVirtualScanCols(SPhysiPlanContext* pCxt, SVirtualScanPhysiNode* pScanPhysiNode, SNodeList* pScanCols) {
5246
0
  if (NULL == pScanCols) {
5247
0
    return TSDB_CODE_SUCCESS;
5248
0
  }
5249
5250
0
  pScanPhysiNode->scan.pScanCols = NULL;
5251
0
  int32_t code = nodesCloneList(pScanCols, &pScanPhysiNode->scan.pScanCols);
5252
0
  if (NULL == pScanPhysiNode->scan.pScanCols) {
5253
0
    return code;
5254
0
  }
5255
0
  return sortScanCols(pScanPhysiNode->scan.pScanCols);
5256
0
}
5257
5258
0
static const SColumnNode* getFederatedScanColumn(const SNode* pNode) {
5259
0
  if (pNode != NULL && QUERY_NODE_TARGET == nodeType(pNode)) {
5260
0
    pNode = ((const STargetNode*)pNode)->pExpr;
5261
0
  }
5262
0
  return (pNode != NULL && QUERY_NODE_COLUMN == nodeType(pNode)) ? (const SColumnNode*)pNode : NULL;
5263
0
}
5264
5265
0
static bool federatedOutputNameMatchesScanColumn(const char* pOutputName, const SColumnNode* pCol) {
5266
0
  if (pOutputName == NULL || pOutputName[0] == '\0' || pCol == NULL) {
5267
0
    return false;
5268
0
  }
5269
5270
0
  const char* pRefName = pCol->hasRef && pCol->refColName[0] != '\0' ? pCol->refColName : pCol->colName;
5271
0
  return (pRefName[0] != '\0' && strcasecmp(pOutputName, pRefName) == 0) ||
5272
0
         (pCol->colName[0] != '\0' && strcasecmp(pOutputName, pCol->colName) == 0) ||
5273
0
         (pCol->node.aliasName[0] != '\0' && strcasecmp(pOutputName, pCol->node.aliasName) == 0) ||
5274
0
         (pCol->node.userAlias[0] != '\0' && strcasecmp(pOutputName, pCol->node.userAlias) == 0);
5275
0
}
5276
5277
static int16_t getFederatedOutputSlot(const SFederatedScanPhysiNode* pScan, const SColumnNode* pCol,
5278
0
                                      int32_t outputIdx) {
5279
0
  if (pScan == NULL || pCol == NULL || pScan->pColTypeMappings == NULL ||
5280
0
      outputIdx < 0 || outputIdx >= pScan->numColTypeMappings || pScan->node.pOutputDataBlockDesc == NULL ||
5281
0
      pScan->node.pOutputDataBlockDesc->pSlots == NULL ||
5282
0
      outputIdx >= LIST_LENGTH(pScan->node.pOutputDataBlockDesc->pSlots)) {
5283
0
    return -1;
5284
0
  }
5285
5286
0
  const SExtColTypeMapping* pMapping = &pScan->pColTypeMappings[outputIdx];
5287
0
  SSlotDescNode* pSlot = (SSlotDescNode*)nodesListGetNode(pScan->node.pOutputDataBlockDesc->pSlots, outputIdx);
5288
0
  if (pMapping->tdType.type != pCol->node.resType.type ||
5289
0
      !federatedOutputNameMatchesScanColumn(pMapping->colName, pCol) || pSlot == NULL || !pSlot->output ||
5290
0
      pSlot->reserve) {
5291
0
    return -1;
5292
0
  }
5293
0
  return pSlot->slotId;
5294
0
}
5295
5296
0
static int32_t prioritizeFederatedOutputSlot(SSlotIndex* pIndex, int16_t outputSlotId) {
5297
0
  int32_t code = TSDB_CODE_SUCCESS;
5298
5299
0
  if (pIndex == NULL || pIndex->pSlotIdsInfo == NULL || taosArrayGetSize(pIndex->pSlotIdsInfo) == 0) {
5300
0
    PLAN_ERR_JRET(TSDB_CODE_INVALID_PARA);
5301
0
  }
5302
5303
0
  SSlotIdInfo* pFirst = taosArrayGet(pIndex->pSlotIdsInfo, 0);
5304
0
  if (pFirst->slotId == outputSlotId) {
5305
0
    return TSDB_CODE_SUCCESS;
5306
0
  }
5307
5308
0
  for (int32_t i = 1; i < taosArrayGetSize(pIndex->pSlotIdsInfo); ++i) {
5309
0
    SSlotIdInfo* pInfo = taosArrayGet(pIndex->pSlotIdsInfo, i);
5310
0
    if (pInfo->slotId == outputSlotId) {
5311
0
      SSlotIdInfo tmp = *pFirst;
5312
0
      *pFirst = *pInfo;
5313
0
      *pInfo = tmp;
5314
0
      return TSDB_CODE_SUCCESS;
5315
0
    }
5316
0
  }
5317
5318
0
  SSlotIdInfo oldFirst = *pFirst;
5319
0
  SSlotIdInfo outputInfo = {.slotId = outputSlotId, .set = false};
5320
0
  if (NULL == taosArrayPush(pIndex->pSlotIdsInfo, &oldFirst)) {
5321
0
    PLAN_ERR_JRET(terrno);
5322
0
  }
5323
0
  pFirst = taosArrayGet(pIndex->pSlotIdsInfo, 0);
5324
0
  *pFirst = outputInfo;
5325
0
  return code;
5326
5327
0
_return:
5328
0
  planError("%s failed since %s", __func__, tstrerror(code));
5329
0
  return code;
5330
0
}
5331
5332
0
static int32_t registerVtableFederatedOutputSlots(SPhysiPlanContext* pCxt, SFederatedScanPhysiNode* pScan) {
5333
0
  int32_t code = TSDB_CODE_SUCCESS;
5334
0
  int32_t lino = 0;
5335
0
  char*   pKey = NULL;
5336
5337
0
  if (pCxt == NULL || pScan == NULL || pScan->pScanCols == NULL || pScan->node.pOutputDataBlockDesc == NULL) {
5338
0
    return TSDB_CODE_INVALID_PARA;
5339
0
  }
5340
5341
0
  SHashObj* pHash = taosArrayGetP(pCxt->pLocationHelper, pScan->node.pOutputDataBlockDesc->dataBlockId);
5342
0
  QUERY_CHECK_NULL(pHash, code, lino, _return, terrno);
5343
5344
  // createFederatedScanPhysiNode keeps scan columns, type mappings, and output
5345
  // slots in the same order; reserve-only scan columns are appended last.
5346
0
  int32_t outputIdx = 0;
5347
0
  SNode*  pNode = NULL;
5348
0
  FOREACH(pNode, pScan->pScanCols) {
5349
0
    const SNode* pScanExpr = pNode;
5350
0
    if (NULL != pScanExpr && QUERY_NODE_TARGET == nodeType(pScanExpr)) {
5351
0
      pScanExpr = ((const STargetNode*)pScanExpr)->pExpr;
5352
0
    }
5353
0
    const SColumnNode* pCol = getFederatedScanColumn(pNode);
5354
0
    int16_t            outputSlotId = -1;
5355
0
    bool               mappedOutput = outputIdx < pScan->numColTypeMappings;
5356
0
    if (mappedOutput) {
5357
0
      outputSlotId = getFederatedOutputSlot(pScan, pCol, outputIdx);
5358
0
    } else {
5359
0
      SSlotDescNode* pSlot =
5360
0
          (SSlotDescNode*)nodesListGetNode(pScan->node.pOutputDataBlockDesc->pSlots, outputIdx);
5361
0
      outputSlotId = NULL != pSlot ? pSlot->slotId : -1;
5362
0
    }
5363
0
    ++outputIdx;
5364
0
    if (NULL == pScanExpr || outputSlotId < 0 || (mappedOutput && NULL == pCol)) {
5365
0
      planError("Invalid federated scan expression or output slot, expr=%p, outputSlotId=%d", pScanExpr,
5366
0
                outputSlotId);
5367
0
      PLAN_ERR_JRET(TSDB_CODE_INVALID_PARA);
5368
0
    }
5369
5370
0
    int32_t keyLen = 0;
5371
0
    int32_t cap = 0;
5372
0
    PLAN_ERR_JRET(getSlotKey((SNode*)pScanExpr, NULL, &pKey, &keyLen, 0, &cap));
5373
5374
0
    SSlotIndex* pIndex = taosHashGet(pHash, pKey, keyLen);
5375
0
    if (pIndex == NULL) {
5376
0
      code = putSlotToHashImpl(pScan->node.pOutputDataBlockDesc->dataBlockId, outputSlotId, pKey, keyLen, pHash);
5377
0
    } else {
5378
0
      code = prioritizeFederatedOutputSlot(pIndex, outputSlotId);
5379
0
    }
5380
0
    taosMemoryFreeClear(pKey);
5381
0
    PLAN_ERR_JRET(code);
5382
0
  }
5383
0
  return code;
5384
5385
0
_return:
5386
0
  taosMemoryFree(pKey);
5387
0
  planError("%s failed since %s", __func__, tstrerror(code));
5388
0
  return code;
5389
0
}
5390
5391
static int32_t initializeDynamicVtableFederatedOutput(SPhysiPlanContext* pCxt,
5392
                                                      const SVirtualScanLogicNode* pVirtualScan,
5393
0
                                                      SFederatedScanPhysiNode* pFederatedScan) {
5394
0
  int32_t code = TSDB_CODE_SUCCESS;
5395
5396
0
  if (NULL == pCxt || NULL == pVirtualScan || NULL == pFederatedScan ||
5397
0
      NULL == pFederatedScan->node.pOutputDataBlockDesc ||
5398
0
      (NULL != pFederatedScan->pScanCols && LIST_LENGTH(pFederatedScan->pScanCols) > 0)) {
5399
0
    return TSDB_CODE_SUCCESS;
5400
0
  }
5401
5402
0
  PLAN_ERR_JRET(nodesCloneList(pVirtualScan->pScanCols, &pFederatedScan->pScanCols));
5403
0
  if (NULL == pFederatedScan->pScanCols) {
5404
0
    PLAN_ERR_JRET(TSDB_CODE_PLAN_INVALID_FEDERATED_SCAN);
5405
0
  }
5406
5407
  // A dynamic FederatedScan chooses its real table and SELECT list only after
5408
  // DynQueryCtrl supplies a batch.  Its physical template still needs the
5409
  // complete virtual-row schema, though: every table in the batch is
5410
  // normalized into this schema before it reaches VirtualScan.  Without this
5411
  // common shape, the second table in a source batch inherits the first
5412
  // table's slot mapping and writes values into the wrong virtual column.
5413
0
  PLAN_ERR_JRET(addDataBlockSlotsImpl(pCxt, pFederatedScan->pScanCols,
5414
0
                                      pFederatedScan->node.pOutputDataBlockDesc,
5415
0
                                      NULL, true, false));
5416
5417
0
_return:
5418
0
  if (TSDB_CODE_SUCCESS != code) {
5419
0
    planError("%s failed since %s", __func__, tstrerror(code));
5420
0
  }
5421
0
  return code;
5422
0
}
5423
5424
static int32_t createVirtualTableScanPhysiNodeFinalize(SPhysiPlanContext* pCxt,
5425
                                                       SNodeList* pChild,
5426
                                                       SVirtualScanLogicNode* pScanLogicNode,
5427
                                                       SVirtualScanPhysiNode* pScanPhysiNode,
5428
0
                                                       SPhysiNode** pPhyNode) {
5429
0
  int32_t code = TSDB_CODE_SUCCESS;
5430
0
  SNodeList* pCondCols = NULL;
5431
0
  SNodeList* pCondScanCols = NULL;
5432
5433
0
  PLAN_ERR_JRET(createVirtualScanCols(pCxt, pScanPhysiNode, pScanLogicNode->pScanCols));
5434
0
  PLAN_ERR_JRET(addDataBlockSlots(pCxt, pScanPhysiNode->scan.pScanCols, pScanPhysiNode->scan.node.pOutputDataBlockDesc));
5435
0
  if (NULL != pScanLogicNode->pScanPseudoCols) {
5436
0
    pScanPhysiNode->scan.pScanPseudoCols = NULL;
5437
0
    PLAN_ERR_JRET(nodesCloneList(pScanLogicNode->pScanPseudoCols, &pScanPhysiNode->scan.pScanPseudoCols));
5438
0
    PLAN_ERR_JRET(addDataBlockSlots(pCxt, pScanPhysiNode->scan.pScanPseudoCols, pScanPhysiNode->scan.node.pOutputDataBlockDesc));
5439
0
  }
5440
5441
0
  if (NULL != pScanLogicNode->node.pConditions) {
5442
0
    PLAN_ERR_JRET(nodesCollectColumnsFromNode(pScanLogicNode->node.pConditions, NULL, COLLECT_COL_TYPE_ALL, &pCondCols));
5443
0
    if (pCondCols != NULL) {
5444
0
      PLAN_ERR_JRET(nodesCloneList(pCondCols, &pCondScanCols));
5445
0
    }
5446
0
    PLAN_ERR_JRET(addDataBlockSlots(pCxt, pCondCols, pScanPhysiNode->scan.node.pOutputDataBlockDesc));
5447
0
  }
5448
5449
0
  PLAN_ERR_JRET(setConditionsSlotId(pCxt, (const SLogicNode*)pScanLogicNode, (SPhysiNode*)pScanPhysiNode));
5450
5451
0
  pScanPhysiNode->scan.uid = pScanLogicNode->tableId;
5452
0
  pScanPhysiNode->scan.suid = pScanLogicNode->stableId;
5453
0
  pScanPhysiNode->scan.tableType = pScanLogicNode->tableType;
5454
0
  memcpy(&pScanPhysiNode->scan.tableName, &pScanLogicNode->tableName, sizeof(SName));
5455
0
  pScanPhysiNode->scanAllCols = pScanLogicNode->scanAllCols;
5456
  // clone the const-ref tag list right after the scalar fields and BEFORE the sibling
5457
  // list clones (pTagRefSources/pLocalTags) that follow -- not before the slot
5458
  // machinery above (createVirtualScanCols/addDataBlockSlots/setConditionsSlotId may
5459
  // already have failed by this point); the physi node must own its own copy.
5460
  // Unlike pRefTagCols, this list deliberately gets NO setMultiBlockSlotId rewrite in
5461
  // createVirtualTableScanPhysiNode: its only runtime consumer (ensureRefTagsResolved)
5462
  // reads logical identity fields only (colId, refDb/Table/ColName, resType) and never
5463
  // slotId/dataBlockId -- output placement flows through scan.pScanPseudoCols' TARGET
5464
  // slots (assigned by addDataBlockSlots above) and findResolvedTagVal's colId lookup.
5465
  // A rewrite would fail outright anyway: no child operator emits const-ref tags (by
5466
  // design no TagRefSource scan node is built for them), so the merged-children slot
5467
  // hash lookup would raise TSDB_CODE_PLAN_SLOT_NOT_FOUND.
5468
0
  if (pScanLogicNode->pConstRefTags) {
5469
0
    PLAN_ERR_JRET(nodesCloneList(pScanLogicNode->pConstRefTags, &pScanPhysiNode->pConstRefTags));
5470
0
  }
5471
5472
  // Clone TagRef fields from logic node to physical node
5473
0
  pScanPhysiNode->hasTagRef = pScanLogicNode->hasTagRef;
5474
0
  pScanPhysiNode->hasLocalTag = pScanLogicNode->hasLocalTag;
5475
0
  pScanPhysiNode->hasConstRefTag = pScanLogicNode->hasConstRefTag;
5476
0
  pScanPhysiNode->mgmtEpSet = pCxt->pPlanCxt->mgmtEpSet;  // for const-ref chain remote hops
5477
5478
0
  if (pScanLogicNode->pTagRefSources) {
5479
0
    PLAN_ERR_JRET(nodesCloneList(pScanLogicNode->pTagRefSources, &pScanPhysiNode->pTagRefSources));
5480
0
  }
5481
0
  if (pScanLogicNode->pLocalTags) {
5482
0
    PLAN_ERR_JRET(nodesCloneList(pScanLogicNode->pLocalTags, &pScanPhysiNode->pLocalTags));
5483
0
  }
5484
0
  if (NULL == pScanPhysiNode->pLocalTags || LIST_LENGTH(pScanPhysiNode->pLocalTags) <= 0) {
5485
0
    if (pScanPhysiNode->pLocalTags != NULL && LIST_LENGTH(pScanPhysiNode->pLocalTags) <= 0) {
5486
0
      nodesDestroyList(pScanPhysiNode->pLocalTags);
5487
0
      pScanPhysiNode->pLocalTags = NULL;
5488
0
    }
5489
0
    PLAN_ERR_JRET(appendPhysiLocalTagsFromList(pScanLogicNode->pScanPseudoCols, &pScanPhysiNode->pLocalTags));
5490
0
    pScanPhysiNode->hasLocalTag = (pScanPhysiNode->pLocalTags != NULL && LIST_LENGTH(pScanPhysiNode->pLocalTags) > 0);
5491
0
  }
5492
0
  if (pScanLogicNode->pRefTagCols) {
5493
0
    PLAN_ERR_JRET(nodesCloneList(pScanLogicNode->pRefTagCols, &pScanPhysiNode->pRefTagCols));
5494
0
  }
5495
0
  if (pScanLogicNode->pTagFilterCond) {
5496
0
    PLAN_ERR_JRET(nodesCloneNode(pScanLogicNode->pTagFilterCond, &pScanPhysiNode->pTagFilterCond));
5497
0
  }
5498
5499
0
  SNode*  pNode = NULL;
5500
0
  int32_t condIdx = 0;
5501
0
  FOREACH(pNode, pCondScanCols) {
5502
0
    if (nodeType(pNode) != QUERY_NODE_COLUMN) {
5503
0
      ++condIdx;
5504
0
      continue;
5505
0
    }
5506
5507
0
    SColumnNode* pCondCol = (SColumnNode*)pNode;
5508
0
    if (pCondCol->colType == COLUMN_TYPE_TAG) {
5509
0
      if (pCondCol->hasRef) {
5510
0
        PLAN_ERR_JRET(appendPhysiScanColumn(&pScanPhysiNode->pRefTagCols, pCondCol));
5511
0
      } else {
5512
0
        PLAN_ERR_JRET(appendPhysiScanColumn(&pScanPhysiNode->scan.pScanPseudoCols, pCondCol));
5513
0
      }
5514
0
    } else {
5515
0
      PLAN_ERR_JRET(appendPhysiScanColumn(&pScanPhysiNode->scan.pScanCols, pCondCol));
5516
0
    }
5517
0
    ++condIdx;
5518
0
  }
5519
5520
  // Mark every federate-scan downstream as feeding a virtual-table scan so
5521
  // the executor can gate vtable-specific behavior (e.g. per-block TIMESTAMP
5522
  // precision conversion) at plan time without the parent operator reaching
5523
  // into child state at runtime.
5524
0
  if (pChild != NULL) {
5525
0
    SNode* pChildNode = NULL;
5526
0
    FOREACH(pChildNode, pChild) {
5527
0
      if (pChildNode != NULL &&
5528
0
          nodeType(pChildNode) == QUERY_NODE_PHYSICAL_PLAN_FEDERATED_SCAN) {
5529
0
        SFederatedScanPhysiNode* pFedScan = (SFederatedScanPhysiNode*)pChildNode;
5530
0
        pFedScan->underVTableScan = true;
5531
0
        PLAN_ERR_JRET(initializeDynamicVtableFederatedOutput(pCxt, pScanLogicNode, pFedScan));
5532
0
        PLAN_ERR_JRET(registerVtableFederatedOutputSlots(pCxt, pFedScan));
5533
0
      }
5534
0
    }
5535
0
  }
5536
0
  *pPhyNode = (SPhysiNode*)pScanPhysiNode;
5537
0
  nodesDestroyList(pCondCols);
5538
0
  nodesDestroyList(pCondScanCols);
5539
0
  return code;
5540
5541
0
_return:
5542
0
  nodesDestroyList(pCondCols);
5543
0
  nodesDestroyList(pCondScanCols);
5544
0
  nodesDestroyNode((SNode*)pScanPhysiNode);
5545
0
  return code;
5546
0
}
5547
5548
static int32_t createVirtualTableScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan, SNodeList* pChildren,
5549
0
                                               SVirtualScanLogicNode* pScanLogicNode, SPhysiNode** pPhyNode) {
5550
0
  int32_t                 code = TSDB_CODE_SUCCESS;
5551
0
  SVirtualScanPhysiNode * pVirtualScan =
5552
0
      (SVirtualScanPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pScanLogicNode, QUERY_NODE_PHYSICAL_PLAN_VIRTUAL_TABLE_SCAN);
5553
0
  if (NULL == pVirtualScan) {
5554
0
    return terrno;
5555
0
  }
5556
5557
0
  pVirtualScan->twoPassMode = pScanLogicNode->twoPassMode;
5558
5559
0
  if (pScanLogicNode->pVgroupList) {
5560
0
    vgroupInfoToNodeAddr(pScanLogicNode->pVgroupList->vgroups, &pSubplan->execNode);
5561
0
    pSubplan->execNodeStat.tableNum = pScanLogicNode->pVgroupList->vgroups[0].numOfTable;
5562
0
  }
5563
5564
0
  (void)tNameGetFullDbName(&pScanLogicNode->tableName, pSubplan->dbFName);
5565
0
  PLAN_ERR_RET(createVirtualTableScanPhysiNodeFinalize(pCxt, pChildren, pScanLogicNode, (SVirtualScanPhysiNode*)pVirtualScan, pPhyNode));
5566
0
  PLAN_ERR_RET(setMultiBlockSlotId(pCxt, pChildren, true, pScanLogicNode->node.pTargets, &pVirtualScan->pTargets));
5567
0
  if (pVirtualScan->pRefTagCols != NULL) {
5568
0
    nodesDestroyList(pVirtualScan->pRefTagCols);
5569
0
    pVirtualScan->pRefTagCols = NULL;
5570
0
  }
5571
0
  PLAN_ERR_RET(setMultiBlockSlotId(pCxt, pChildren, true, pScanLogicNode->pRefTagCols, &pVirtualScan->pRefTagCols));
5572
0
  return code;
5573
0
}
5574
5575
static int32_t createGroupCachePhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
5576
0
                                         SGroupCacheLogicNode* pLogicNode, SPhysiNode** pPhyNode) {
5577
0
  SGroupCachePhysiNode* pGrpCache =
5578
0
      (SGroupCachePhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pLogicNode, QUERY_NODE_PHYSICAL_PLAN_GROUP_CACHE);
5579
0
  if (NULL == pGrpCache) {
5580
0
    return terrno;
5581
0
  }
5582
5583
0
  pGrpCache->grpColsMayBeNull = pLogicNode->grpColsMayBeNull;
5584
0
  pGrpCache->grpByUid = pLogicNode->grpByUid;
5585
0
  pGrpCache->globalGrp = pLogicNode->globalGrp;
5586
0
  pGrpCache->batchFetch = pLogicNode->batchFetch;
5587
0
  pGrpCache->twoPassMode = pLogicNode->twoPassMode;
5588
0
  SDataBlockDescNode* pChildDesc = ((SPhysiNode*)nodesListGetNode(pChildren, 0))->pOutputDataBlockDesc;
5589
0
  int32_t             code = TSDB_CODE_SUCCESS;
5590
  /*
5591
    if (TSDB_CODE_SUCCESS == code) {
5592
      code = setListSlotId(pCxt, pChildDesc->dataBlockId, -1, pLogicNode->pGroupCols, &pGrpCache->pGroupCols);
5593
    }
5594
  */
5595
5596
0
  *pPhyNode = (SPhysiNode*)pGrpCache;
5597
5598
0
  return code;
5599
0
}
5600
5601
static int32_t createTagRefSourcePhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan, SNodeList* pChildren,
5602
0
                                           STagRefSourceLogicNode* pLogicNode, SPhysiNode** pPhyNode) {
5603
0
  int32_t code = TSDB_CODE_SUCCESS;
5604
0
  STagRefSourcePhysiNode* pTagRefSource =
5605
0
      (STagRefSourcePhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pLogicNode, QUERY_NODE_PHYSICAL_PLAN_TAG_REF_SOURCE);
5606
0
  if (NULL == pTagRefSource) {
5607
0
    return terrno;
5608
0
  }
5609
5610
  // Copy source table information
5611
0
  memcpy(&pTagRefSource->sourceTableName, &pLogicNode->sourceTableName, sizeof(SName));
5612
0
  pTagRefSource->sourceSuid = pLogicNode->sourceSuid;
5613
0
  pTagRefSource->sourceId = pLogicNode->sourceId;
5614
0
  pTagRefSource->isUsedInFilter = pLogicNode->isUsedInFilter;
5615
0
  pTagRefSource->isUsedInProjection = pLogicNode->isUsedInProjection;
5616
5617
  // Clone referenced columns
5618
0
  if (pLogicNode->pRefCols) {
5619
0
    PLAN_ERR_JRET(nodesCloneList(pLogicNode->pRefCols, &pTagRefSource->pRefCols));
5620
0
  }
5621
5622
  // Clone vgroup list to physical node
5623
  // Note: After split phase, pVgroupList is transferred from logic node to logic subplan via TSWAP
5624
  // The execNode should already be set in createPhysiSubplan from the logic subplan's vgroup list
5625
0
  if (pLogicNode->pVgroupList) {
5626
    // vgroup list is still in the logic node (no split or special case)
5627
0
    int32_t numOfVgroups = pLogicNode->pVgroupList->numOfVgroups;
5628
0
    pTagRefSource->pVgroupList = taosMemoryCalloc(1, sizeof(SVgroupsInfo) + numOfVgroups * sizeof(SVgroupInfo));
5629
0
    if (NULL == pTagRefSource->pVgroupList) {
5630
0
      PLAN_ERR_JRET(TSDB_CODE_OUT_OF_MEMORY);
5631
0
    }
5632
0
    pTagRefSource->pVgroupList->numOfVgroups = numOfVgroups;
5633
0
    memcpy(pTagRefSource->pVgroupList->vgroups, pLogicNode->pVgroupList->vgroups, numOfVgroups * sizeof(SVgroupInfo));
5634
5635
    // Set subplan execNode from vgroup info for scheduling
5636
0
    vgroupInfoToNodeAddr(pLogicNode->pVgroupList->vgroups, &pSubplan->execNode);
5637
0
    pSubplan->execNodeStat.tableNum = pLogicNode->pVgroupList->vgroups[0].numOfTable;
5638
0
  } else {
5639
    // vgroup list was transferred to logic subplan during split
5640
    // execNode should already be set in createPhysiSubplan
5641
    // We still create a minimal vgroup list for the physical node if needed
5642
0
    if (pSubplan->execNode.nodeId > 0) {
5643
0
      pTagRefSource->pVgroupList = taosMemoryCalloc(1, sizeof(SVgroupsInfo) + sizeof(SVgroupInfo));
5644
0
      if (NULL == pTagRefSource->pVgroupList) {
5645
0
        PLAN_ERR_JRET(TSDB_CODE_OUT_OF_MEMORY);
5646
0
      }
5647
0
      pTagRefSource->pVgroupList->numOfVgroups = 1;
5648
      // Reverse the vgroupInfoToNodeAddr conversion to populate vgroup info
5649
0
      pTagRefSource->pVgroupList->vgroups[0].vgId = pSubplan->execNode.nodeId;
5650
0
      pTagRefSource->pVgroupList->vgroups[0].epSet = pSubplan->execNode.epSet;
5651
      // tableNum should already be set in execNodeStat
5652
0
    }
5653
0
  }
5654
5655
  // Create scan columns from referenced columns for source table scan.
5656
  // IMPORTANT: pScanCols must use hasRef=true with the same refDbName/refTableName/refColName
5657
  // as the pTargets columns. getSlotKey() uses these ref fields to generate the slot key,
5658
  // so matching keys ensure addDataBlockSlots reuses existing slots from pTargets
5659
  // rather than creating duplicate slots with different keys.
5660
0
  if (pLogicNode->pRefCols) {
5661
0
    PLAN_ERR_JRET(nodesMakeList(&pTagRefSource->pScanCols));
5662
0
    SNode* pRefCol = NULL;
5663
0
    FOREACH(pRefCol, pLogicNode->pRefCols) {
5664
0
      STagRefColumn* pTagRef = (STagRefColumn*)pRefCol;
5665
0
      SColumnNode* pScanCol = NULL;
5666
0
      PLAN_ERR_JRET(nodesMakeNode(QUERY_NODE_COLUMN, (SNode**)&pScanCol));
5667
0
      pScanCol->tableId = pLogicNode->sourceSuid;
5668
0
      pScanCol->colId = pTagRef->sourceColId;
5669
0
      pScanCol->colType = COLUMN_TYPE_TAG;
5670
0
      pScanCol->node.resType.type = pTagRef->dataType;
5671
0
      pScanCol->node.resType.bytes = pTagRef->bytes;
5672
0
      tstrncpy(pScanCol->colName, pTagRef->colName, TSDB_COL_NAME_LEN);
5673
0
      tstrncpy(pScanCol->dbName, pLogicNode->sourceTableName.dbname, TSDB_DB_NAME_LEN);
5674
0
      tstrncpy(pScanCol->tableName, pLogicNode->sourceTableName.tname, TSDB_TABLE_NAME_LEN);
5675
      // tableAlias must be non-empty so getSlotKey reaches the hasRef branch
5676
      // (empty tableAlias causes early return with just colName as key)
5677
0
      tstrncpy(pScanCol->tableAlias, pLogicNode->sourceTableName.tname, TSDB_TABLE_NAME_LEN);
5678
0
      pScanCol->hasRef = true;
5679
0
      tstrncpy(pScanCol->refDbName, pLogicNode->sourceTableName.dbname, TSDB_DB_NAME_LEN);
5680
0
      tstrncpy(pScanCol->refTableName, pLogicNode->sourceTableName.tname, TSDB_TABLE_NAME_LEN);
5681
0
      tstrncpy(pScanCol->refColName, pTagRef->sourceColName, TSDB_COL_NAME_LEN);
5682
0
      PLAN_ERR_JRET(nodesListStrictAppend(pTagRefSource->pScanCols, (SNode*)pScanCol));
5683
0
    }
5684
0
  }
5685
5686
  // addDataBlockSlots reuses existing slots from pTargets since slot keys match
5687
0
  if (pTagRefSource->pScanCols) {
5688
0
    PLAN_ERR_JRET(addDataBlockSlots(pCxt, pTagRefSource->pScanCols, pTagRefSource->node.pOutputDataBlockDesc));
5689
0
  }
5690
5691
  // Set conditions slot id
5692
0
  PLAN_ERR_JRET(setConditionsSlotId(pCxt, (const SLogicNode*)pLogicNode, (SPhysiNode*)pTagRefSource));
5693
5694
0
  *pPhyNode = (SPhysiNode*)pTagRefSource;
5695
0
  return code;
5696
5697
0
_return:
5698
0
  nodesDestroyNode((SNode*)pTagRefSource);
5699
0
  return code;
5700
0
}
5701
5702
static int32_t updateDynQueryCtrlStbJoinInfo(SPhysiPlanContext* pCxt, SNodeList* pChildren,
5703
0
                                             SDynQueryCtrlLogicNode* pLogicNode, SDynQueryCtrlPhysiNode* pDynCtrl) {
5704
0
  SDataBlockDescNode* pPrevDesc = ((SPhysiNode*)nodesListGetNode(pChildren, 0))->pOutputDataBlockDesc;
5705
0
  SNodeList*          pVgList = NULL;
5706
0
  SNodeList*          pUidList = NULL;
5707
0
  int32_t             code = setListSlotId(pCxt, pPrevDesc->dataBlockId, -1, pLogicNode->stbJoin.pVgList, &pVgList);
5708
0
  if (TSDB_CODE_SUCCESS == code) {
5709
0
    code = setListSlotId(pCxt, pPrevDesc->dataBlockId, -1, pLogicNode->stbJoin.pUidList, &pUidList);
5710
0
  }
5711
0
  if (TSDB_CODE_SUCCESS == code) {
5712
0
    memcpy(pDynCtrl->stbJoin.srcScan, pLogicNode->stbJoin.srcScan, sizeof(pDynCtrl->stbJoin.srcScan));
5713
5714
0
    SNode*  pNode = NULL;
5715
0
    int32_t i = 0;
5716
0
    FOREACH(pNode, pVgList) {
5717
0
      pDynCtrl->stbJoin.vgSlot[i] = ((SColumnNode*)pNode)->slotId;
5718
0
      ++i;
5719
0
    }
5720
0
    i = 0;
5721
0
    FOREACH(pNode, pUidList) {
5722
0
      pDynCtrl->stbJoin.uidSlot[i] = ((SColumnNode*)pNode)->slotId;
5723
0
      ++i;
5724
0
    }
5725
0
    pDynCtrl->stbJoin.batchFetch = pLogicNode->stbJoin.batchFetch;
5726
0
  }
5727
0
  nodesDestroyList(pVgList);
5728
0
  nodesDestroyList(pUidList);
5729
5730
0
  return code;
5731
0
}
5732
5733
static int32_t updateDynQueryCtrlVtbScanInfo(SPhysiPlanContext* pCxt, SNodeList* pChildren,
5734
                                             SDynQueryCtrlLogicNode* pLogicNode, SDynQueryCtrlPhysiNode* pDynCtrl,
5735
0
                                             SSubplan* pSubPlan) {
5736
0
  int32_t code = TSDB_CODE_SUCCESS;
5737
5738
0
  if (pLogicNode->vtbScan.pVgroupList) {
5739
0
    vgroupInfoToNodeAddr(pLogicNode->vtbScan.pVgroupList->vgroups, &pSubPlan->execNode);
5740
0
    pSubPlan->execNodeStat.tableNum = pLogicNode->vtbScan.pVgroupList->vgroups[0].numOfTable;
5741
0
  }
5742
5743
0
  TSWAP(pLogicNode->vtbScan.pOrgVgIds, pDynCtrl->vtbScan.pOrgVgIds);
5744
0
  if (pLogicNode->vtbScan.pScanCols) {
5745
0
    PLAN_ERR_JRET(nodesCloneList(pLogicNode->vtbScan.pScanCols, &pDynCtrl->vtbScan.pScanCols));
5746
0
  } else {
5747
0
    PLAN_ERR_JRET(nodesCloneList(pLogicNode->node.pTargets, &pDynCtrl->vtbScan.pScanCols));
5748
0
  }
5749
5750
0
  pDynCtrl->vtbScan.scanAllCols = pLogicNode->vtbScan.scanAllCols;
5751
0
  pDynCtrl->vtbScan.suid = pLogicNode->vtbScan.suid;
5752
0
  pDynCtrl->vtbScan.uid = pLogicNode->vtbScan.uid;
5753
0
  pDynCtrl->vtbScan.isSuperTable = pLogicNode->vtbScan.isSuperTable;
5754
0
  pDynCtrl->vtbScan.hasLocalTag = false;
5755
0
  pDynCtrl->vtbScan.batchProcessChild = pLogicNode->vtbScan.batchProcessChild;
5756
0
  pDynCtrl->vtbScan.hasPartition = pLogicNode->vtbScan.hasPartition;
5757
0
  pDynCtrl->vtbScan.rversion = pLogicNode->vtbScan.rversion;
5758
0
  pDynCtrl->vtbScan.mgmtEpSet = pCxt->pPlanCxt->mgmtEpSet;
5759
0
  pDynCtrl->vtbScan.accountId = pCxt->pPlanCxt->acctId;
5760
0
  tstrncpy(pDynCtrl->vtbScan.dbName, pLogicNode->vtbScan.dbName, TSDB_DB_NAME_LEN);
5761
0
  tstrncpy(pDynCtrl->vtbScan.tbName, pLogicNode->vtbScan.tbName, TSDB_TABLE_NAME_LEN);
5762
5763
  // Propagate tag-ref filter info from VirtualScanLogicNode (logic node's child, still intact)
5764
  // NOTE: pChildren (physi) are Exchange nodes after plan splitting - VirtualTableScan is NOT there.
5765
  // We access the logic tree directly to get pTagFilterCond and pRefTagCols.
5766
0
  SNode* pLogicChild = NULL;
5767
0
  FOREACH(pLogicChild, pLogicNode->node.pChildren) {
5768
0
    if (nodeType(pLogicChild) == QUERY_NODE_LOGIC_PLAN_VIRTUAL_TABLE_SCAN) {
5769
0
      SVirtualScanLogicNode* pVScanLogic = (SVirtualScanLogicNode*)pLogicChild;
5770
0
      if (pVScanLogic->pTagFilterCond) {
5771
0
        PLAN_ERR_JRET(nodesCloneNode(pVScanLogic->pTagFilterCond, &pDynCtrl->vtbScan.pTagFilterCond));
5772
0
      }
5773
0
      if (pVScanLogic->pRefTagCols) {
5774
0
        PLAN_ERR_JRET(nodesCloneList(pVScanLogic->pRefTagCols, &pDynCtrl->vtbScan.pRefTagCols));
5775
0
      }
5776
0
      pDynCtrl->vtbScan.hasLocalTag = pVScanLogic->hasLocalTag;
5777
0
      break;
5778
0
    }
5779
0
  }
5780
  // Copy tag-ref source scalars from logic node (populated before plan splitting)
5781
0
  pDynCtrl->vtbScan.tagRefSourceSuid = pLogicNode->vtbScan.tagRefSourceSuid;
5782
0
  pDynCtrl->vtbScan.tagRefSourceColId = pLogicNode->vtbScan.tagRefSourceColId;
5783
0
  pDynCtrl->vtbScan.tagRefSourceColType = pLogicNode->vtbScan.tagRefSourceColType;
5784
0
  tstrncpy(pDynCtrl->vtbScan.tagRefTerminalColName, pLogicNode->vtbScan.tagRefTerminalColName, TSDB_COL_NAME_LEN);
5785
5786
0
  return code;
5787
0
_return:
5788
0
  planError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(code));
5789
0
  return code;
5790
0
}
5791
5792
static int32_t rewritePrecalcExpr(SPhysiPlanContext* pCxt, SNode* pNode, SNodeList** pPrecalcExprs,
5793
                                  SNode** pRewritten);
5794
static int32_t rewritePrecalcExprs(SPhysiPlanContext* pCxt, SNodeList* pList, SNodeList** pPrecalcExprs,
5795
                                   SNodeList** pRewrittenList);
5796
static int32_t getChildTuple(SDataBlockDescNode **pChildTuple, SNodeList* pChildren);
5797
5798
static int32_t updateDynQueryCtrlVtbWindowInfo(SPhysiPlanContext* pCxt, SNodeList* pChildren,
5799
0
                                               SDynQueryCtrlLogicNode* pLogicNode, SDynQueryCtrlPhysiNode* pDynCtrl) {
5800
0
  int32_t   code = TSDB_CODE_SUCCESS;
5801
5802
0
  pDynCtrl->vtbWindow.wstartSlotId = pLogicNode->vtbWindow.wstartSlotId;
5803
0
  pDynCtrl->vtbWindow.wendSlotId = pLogicNode->vtbWindow.wendSlotId;
5804
0
  pDynCtrl->vtbWindow.wdurationSlotId = pLogicNode->vtbWindow.wdurationSlotId;
5805
0
  pDynCtrl->vtbWindow.isVstb = pLogicNode->vtbWindow.isVstb;
5806
0
  pDynCtrl->vtbWindow.extendOption = pLogicNode->vtbWindow.extendOption;
5807
5808
0
  PLAN_ERR_RET(setMultiBlockSlotId(pCxt, pChildren, false, pLogicNode->node.pTargets, &pDynCtrl->vtbWindow.pTargets));
5809
0
  PLAN_ERR_RET(addDataBlockSlots(pCxt, pDynCtrl->vtbWindow.pTargets, pDynCtrl->node.pOutputDataBlockDesc));
5810
5811
0
  return code;
5812
0
}
5813
5814
0
static int32_t markVtableFederatedScans(SPhysiPlanContext* pCxt, SPhysiNode* pNode) {
5815
0
  int32_t code = TSDB_CODE_SUCCESS;
5816
5817
0
  if (NULL == pNode) {
5818
0
    return TSDB_CODE_INVALID_PARA;
5819
0
  }
5820
0
  if (QUERY_NODE_PHYSICAL_PLAN_FEDERATED_SCAN == nodeType((SNode*)pNode)) {
5821
0
    SFederatedScanPhysiNode* pScan = (SFederatedScanPhysiNode*)pNode;
5822
0
    pScan->underVTableScan = true;
5823
0
    PLAN_ERR_JRET(registerVtableFederatedOutputSlots(pCxt, pScan));
5824
0
    goto _return;
5825
0
  }
5826
5827
0
  SNode* pChild = NULL;
5828
0
  FOREACH(pChild, pNode->pChildren) {
5829
0
    PLAN_ERR_JRET(markVtableFederatedScans(pCxt, (SPhysiNode*)pChild));
5830
0
  }
5831
5832
0
_return:
5833
0
  if (TSDB_CODE_SUCCESS != code) {
5834
0
    planError("%s failed since %s", __func__, tstrerror(code));
5835
0
  }
5836
0
  return code;
5837
0
}
5838
5839
0
static void markVtableWindowFederatedScansFlagOnly(SPhysiNode* pNode) {
5840
0
  if (NULL == pNode) {
5841
0
    return;
5842
0
  }
5843
0
  if (QUERY_NODE_PHYSICAL_PLAN_FEDERATED_SCAN == nodeType((SNode*)pNode)) {
5844
0
    ((SFederatedScanPhysiNode*)pNode)->underVTableScan = true;
5845
0
    return;
5846
0
  }
5847
0
  SNode* pChild = NULL;
5848
0
  FOREACH(pChild, pNode->pChildren) {
5849
0
    markVtableWindowFederatedScansFlagOnly((SPhysiNode*)pChild);
5850
0
  }
5851
0
}
5852
5853
static int32_t createDynQueryCtrlPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
5854
0
                                           SDynQueryCtrlLogicNode* pLogicNode, SPhysiNode** pPhyNode, SSubplan* pSubPlan) {
5855
0
  int32_t                 code = TSDB_CODE_SUCCESS;
5856
0
  SNode*                  pChild = NULL;
5857
0
  SDynQueryCtrlPhysiNode* pDynCtrl =
5858
0
      (SDynQueryCtrlPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pLogicNode, QUERY_NODE_PHYSICAL_PLAN_DYN_QUERY_CTRL);
5859
0
  if (NULL == pDynCtrl) {
5860
0
    PLAN_ERR_JRET(terrno);
5861
0
  }
5862
5863
0
  switch (pLogicNode->qType) {
5864
0
    case DYN_QTYPE_STB_HASH:
5865
0
      PLAN_ERR_JRET(updateDynQueryCtrlStbJoinInfo(pCxt, pChildren, pLogicNode, pDynCtrl));
5866
0
      break;
5867
0
    case DYN_QTYPE_VTB_TS_SCAN:
5868
0
      PLAN_ERR_JRET(updateDynQueryCtrlVtbScanInfo(pCxt, pChildren, pLogicNode, pDynCtrl, pSubPlan));
5869
0
      FOREACH(pChild, pChildren) {
5870
0
        markVtableWindowFederatedScansFlagOnly((SPhysiNode*)pChild);
5871
0
      }
5872
0
      break;
5873
0
    case DYN_QTYPE_VTB_SCAN:
5874
0
      PLAN_ERR_JRET(updateDynQueryCtrlVtbScanInfo(pCxt, pChildren, pLogicNode, pDynCtrl, pSubPlan));
5875
0
      break;
5876
0
    case DYN_QTYPE_VTB_AGG:
5877
0
      PLAN_ERR_JRET(updateDynQueryCtrlVtbScanInfo(pCxt, pChildren, pLogicNode, pDynCtrl, pSubPlan));
5878
0
      FOREACH(pChild, pChildren) {
5879
0
        PLAN_ERR_JRET(markVtableFederatedScans(pCxt, (SPhysiNode*)pChild));
5880
0
      }
5881
0
      break;
5882
0
    case DYN_QTYPE_VTB_INTERVAL:
5883
0
      PLAN_ERR_JRET(updateDynQueryCtrlVtbScanInfo(pCxt, pChildren, pLogicNode, pDynCtrl, pSubPlan));
5884
0
      FOREACH(pChild, pChildren) {
5885
0
        PLAN_ERR_JRET(markVtableFederatedScans(pCxt, (SPhysiNode*)pChild));
5886
0
      }
5887
0
      break;
5888
0
    case DYN_QTYPE_VTB_WINDOW:
5889
0
      PLAN_ERR_JRET(updateDynQueryCtrlVtbScanInfo(pCxt, pChildren, pLogicNode, pDynCtrl, pSubPlan));
5890
0
      PLAN_ERR_JRET(updateDynQueryCtrlVtbWindowInfo(pCxt, pChildren, pLogicNode, pDynCtrl));
5891
0
      FOREACH(pChild, pChildren) {
5892
0
        PLAN_ERR_JRET(markVtableFederatedScans(pCxt, (SPhysiNode*)pChild));
5893
0
      }
5894
0
      break;
5895
0
    default:
5896
0
      PLAN_ERR_JRET(TSDB_CODE_PLAN_INVALID_DYN_CTRL_TYPE);
5897
0
  }
5898
5899
0
  pDynCtrl->qType = pLogicNode->qType;
5900
0
  pDynCtrl->dynTbname = pLogicNode->dynTbname;
5901
0
  *pPhyNode = (SPhysiNode*)pDynCtrl;
5902
5903
0
  return code;
5904
0
_return:
5905
0
  planError("%s failed since %s", __func__, tstrerror(code));
5906
0
  return code;
5907
0
}
5908
5909
typedef struct SRewritePrecalcExprsCxt {
5910
  int32_t    errCode;
5911
  int32_t    planNodeId;
5912
  int32_t    rewriteId;
5913
  SNodeList* pPrecalcExprs;
5914
  SHashObj*  pSeenNames;  // tracks aliasNames already used as slot keys
5915
} SRewritePrecalcExprsCxt;
5916
5917
0
static EDealRes collectAndRewrite(SRewritePrecalcExprsCxt* pCxt, SNode** pNode) {
5918
0
  SNode* pExpr = NULL;
5919
0
  pCxt->errCode = nodesCloneNode(*pNode, &pExpr);
5920
0
  if (NULL == pExpr) {
5921
0
    return DEAL_RES_ERROR;
5922
0
  }
5923
0
  if (TSDB_CODE_SUCCESS != (pCxt->errCode = nodesListAppend(pCxt->pPrecalcExprs, pExpr))) {
5924
0
    nodesDestroyNode(pExpr);
5925
0
    return DEAL_RES_ERROR;
5926
0
  }
5927
0
  SColumnNode* pCol = NULL;
5928
0
  pCxt->errCode = nodesMakeNode(QUERY_NODE_COLUMN, (SNode**)&pCol);
5929
0
  if (NULL == pCol) {
5930
0
    nodesDestroyNode(pExpr);
5931
0
    return DEAL_RES_ERROR;
5932
0
  }
5933
0
  SExprNode* pRewrittenExpr = (SExprNode*)pExpr;
5934
0
  pCol->node.resType = pRewrittenExpr->resType;
5935
0
  if ('\0' == pRewrittenExpr->aliasName[0]) {
5936
0
    snprintf(pRewrittenExpr->aliasName, sizeof(pRewrittenExpr->aliasName), "#expr_%d_%d", pCxt->planNodeId,
5937
0
             pCxt->rewriteId++);
5938
0
  } else if (pCxt->pSeenNames != NULL) {
5939
0
    int32_t nameLen = (int32_t)strlen(pRewrittenExpr->aliasName);
5940
0
    SNode** ppSeenExpr = taosHashGet(pCxt->pSeenNames, pRewrittenExpr->aliasName, nameLen);
5941
0
    if (NULL != ppSeenExpr && !nodesEqualNode(*ppSeenExpr, pExpr)) {
5942
0
      snprintf(pRewrittenExpr->aliasName, sizeof(pRewrittenExpr->aliasName), "#expr_%d_%d", pCxt->planNodeId,
5943
0
               pCxt->rewriteId++);
5944
0
    }
5945
0
  }
5946
0
  int32_t nameLen = (int32_t)strlen(pRewrittenExpr->aliasName);
5947
0
  if (pCxt->pSeenNames != NULL && nameLen > 0 &&
5948
0
      NULL == taosHashGet(pCxt->pSeenNames, pRewrittenExpr->aliasName, nameLen)) {
5949
0
    pCxt->errCode = taosHashPut(pCxt->pSeenNames, pRewrittenExpr->aliasName, nameLen, &pExpr, POINTER_BYTES);
5950
0
    if (TSDB_CODE_SUCCESS != pCxt->errCode) {
5951
0
      nodesDestroyNode((SNode*)pCol);
5952
0
      return DEAL_RES_ERROR;
5953
0
    }
5954
0
  }
5955
0
  tstrncpy(pCol->colName, pRewrittenExpr->aliasName, TSDB_COL_NAME_LEN);
5956
0
  tstrncpy(pCol->node.userAlias, pRewrittenExpr->userAlias, TSDB_COL_NAME_LEN);
5957
0
  nodesDestroyNode(*pNode);
5958
0
  *pNode = (SNode*)pCol;
5959
0
  return DEAL_RES_IGNORE_CHILD;
5960
0
}
5961
5962
0
static int32_t rewriteValueToOperator(SRewritePrecalcExprsCxt* pCxt, SNode** pNode) {
5963
0
  SOperatorNode* pOper = NULL;
5964
0
  int32_t        code = nodesMakeNode(QUERY_NODE_OPERATOR, (SNode**)&pOper);
5965
0
  if (NULL == pOper) {
5966
0
    return code;
5967
0
  }
5968
0
  pOper->pLeft = NULL;
5969
0
  code = nodesMakeNode(QUERY_NODE_LEFT_VALUE, &pOper->pLeft);
5970
0
  if (NULL == pOper->pLeft) {
5971
0
    nodesDestroyNode((SNode*)pOper);
5972
0
    return code;
5973
0
  }
5974
0
  SValueNode* pVal = (SValueNode*)*pNode;
5975
0
  pOper->node.resType = pVal->node.resType;
5976
0
  tstrncpy(pOper->node.aliasName, pVal->node.aliasName, TSDB_COL_NAME_LEN);
5977
0
  tstrncpy(pOper->node.userAlias, pVal->node.userAlias, TSDB_COL_NAME_LEN);
5978
0
  pOper->opType = OP_TYPE_ASSIGN;
5979
0
  pOper->pRight = *pNode;
5980
0
  *pNode = (SNode*)pOper;
5981
0
  return TSDB_CODE_SUCCESS;
5982
0
}
5983
5984
0
static EDealRes doRewritePrecalcExprs(SNode** pNode, void* pContext) {
5985
0
  SRewritePrecalcExprsCxt* pCxt = (SRewritePrecalcExprsCxt*)pContext;
5986
0
  switch (nodeType(*pNode)) {
5987
0
    case QUERY_NODE_VALUE: {
5988
0
      if (((SValueNode*)*pNode)->notReserved) {
5989
0
        break;
5990
0
      }
5991
0
      pCxt->errCode = rewriteValueToOperator(pCxt, pNode);
5992
0
      if (TSDB_CODE_SUCCESS != pCxt->errCode) {
5993
0
        return DEAL_RES_ERROR;
5994
0
      }
5995
0
      return collectAndRewrite(pCxt, pNode);
5996
0
    }
5997
0
    case QUERY_NODE_OPERATOR:
5998
0
    case QUERY_NODE_LOGIC_CONDITION:
5999
0
    case QUERY_NODE_CASE_WHEN: {
6000
0
      return collectAndRewrite(pCxt, pNode);
6001
0
    }
6002
0
    case QUERY_NODE_FUNCTION: {
6003
0
      SFunctionNode* pFunc = (SFunctionNode*)(*pNode);
6004
0
      if (fmIsScalarFunc(pFunc->funcId) && !fmIsNoPushdownFunc(pFunc->funcId) &&
6005
0
          !fmIsVolatileFunc(pFunc->funcId)) {
6006
0
        return collectAndRewrite(pCxt, pNode);
6007
0
      }
6008
0
      break;
6009
0
    }
6010
0
    case QUERY_NODE_REMOTE_VALUE: {
6011
0
      return collectAndRewrite(pCxt, pNode);
6012
0
    }
6013
0
    case QUERY_NODE_CORRELATED_SUBQUERY: {
6014
0
      return collectAndRewrite(pCxt, pNode);
6015
0
    }
6016
0
    default:
6017
0
      break;
6018
0
  }
6019
0
  return DEAL_RES_CONTINUE;
6020
0
}
6021
6022
static int32_t rewritePrecalcExprs(SPhysiPlanContext* pCxt, SNodeList* pList, SNodeList** pPrecalcExprs,
6023
0
                                   SNodeList** pRewrittenList) {
6024
0
  if (NULL == pList) {
6025
0
    return TSDB_CODE_SUCCESS;
6026
0
  }
6027
0
  int32_t code = 0;
6028
0
  if (NULL == *pPrecalcExprs) {
6029
0
    code = nodesMakeList(pPrecalcExprs);
6030
0
    if (NULL == *pPrecalcExprs) {
6031
0
      return code;
6032
0
    }
6033
0
  }
6034
0
  if (NULL == *pRewrittenList) {
6035
0
    code = nodesMakeList(pRewrittenList);
6036
0
    if (NULL == *pRewrittenList) {
6037
0
      return code;
6038
0
    }
6039
0
  }
6040
0
  SNode* pNode = NULL;
6041
0
  FOREACH(pNode, pList) {
6042
0
    SNode* pNew = NULL;
6043
0
    if (QUERY_NODE_GROUPING_SET == nodeType(pNode)) {
6044
0
      code = nodesCloneNode(nodesListGetNode(((SGroupingSetNode*)pNode)->pParameterList, 0), &pNew);
6045
0
    } else {
6046
0
      code = nodesCloneNode(pNode, &pNew);
6047
0
    }
6048
0
    if (NULL == pNew) {
6049
0
      return code;
6050
0
    }
6051
0
    if (TSDB_CODE_SUCCESS != (code = nodesListAppend(*pRewrittenList, pNew))) {
6052
0
      return code;
6053
0
    }
6054
0
  }
6055
0
  SHashObj* pSeenNames =
6056
0
      taosHashInit(LIST_LENGTH(pList), taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), false, HASH_NO_LOCK);
6057
0
  SRewritePrecalcExprsCxt cxt = {.errCode = TSDB_CODE_SUCCESS, .pPrecalcExprs = *pPrecalcExprs,
6058
0
                                 .pSeenNames = pSeenNames};
6059
0
  nodesRewriteExprs(*pRewrittenList, doRewritePrecalcExprs, &cxt);
6060
0
  taosHashCleanup(pSeenNames);
6061
0
  if (0 == LIST_LENGTH(cxt.pPrecalcExprs) || TSDB_CODE_SUCCESS != cxt.errCode) {
6062
0
    NODES_DESTORY_LIST(*pPrecalcExprs);
6063
0
  }
6064
0
  return cxt.errCode;
6065
0
}
6066
6067
static int32_t rewritePrecalcExpr(SPhysiPlanContext* pCxt, SNode* pNode, SNodeList** pPrecalcExprs,
6068
0
                                  SNode** pRewritten) {
6069
0
  if (NULL == pNode) {
6070
0
    return TSDB_CODE_SUCCESS;
6071
0
  }
6072
0
6073
0
  SNodeList* pList = NULL;
6074
0
  int32_t    code = nodesListMakeAppend(&pList, pNode);
6075
0
  SNodeList* pRewrittenList = NULL;
6076
0
  if (TSDB_CODE_SUCCESS == code) {
6077
0
    code = rewritePrecalcExprs(pCxt, pList, pPrecalcExprs, &pRewrittenList);
6078
0
  }
6079
0
  if (TSDB_CODE_SUCCESS == code) {
6080
0
    *pRewritten = nodesListGetNode(pRewrittenList, 0);
6081
0
  }
6082
0
  nodesClearList(pList);
6083
0
  nodesClearList(pRewrittenList);
6084
0
  return code;
6085
0
}
6086
6087
0
static EDealRes hasCountLikeFunc(SNode* pNode, void* res) {
6088
0
  if (QUERY_NODE_FUNCTION == nodeType(pNode)) {
6089
0
    SFunctionNode* pFunc = (SFunctionNode*)pNode;
6090
0
    if (fmIsCountLikeFunc(pFunc->funcId) || (pFunc->hasOriginalFunc && fmIsCountLikeFunc(pFunc->originalFuncId))) {
6091
0
      *(bool*)res = true;
6092
0
      return DEAL_RES_END;
6093
0
    }
6094
0
  }
6095
0
  return DEAL_RES_CONTINUE;
6096
0
}
6097
6098
0
static bool isDynVirtualStableScan(SNode* pNode) {
6099
0
  if (NULL == pNode || QUERY_NODE_PHYSICAL_PLAN_DYN_QUERY_CTRL != nodeType(pNode)) {
6100
0
    return false;
6101
0
  }
6102
0
  SDynQueryCtrlPhysiNode* pDynCtrl = (SDynQueryCtrlPhysiNode*)pNode;
6103
0
  if (DYN_QTYPE_VTB_SCAN != pDynCtrl->qType) {
6104
0
    return false;
6105
0
  }
6106
0
  if (nodesListGetNode(pDynCtrl->node.pChildren, 0)== NULL) {
6107
0
    return false;
6108
0
  }
6109
0
  if (QUERY_NODE_PHYSICAL_PLAN_VIRTUAL_TABLE_SCAN != nodeType(nodesListGetNode(pDynCtrl->node.pChildren, 0))) {
6110
0
    return false;
6111
0
  }
6112
0
  return true;
6113
0
}
6114
6115
0
static bool isDynVirtualStableAgg(SNode* pNode) {
6116
0
  if (NULL == pNode || QUERY_NODE_PHYSICAL_PLAN_DYN_QUERY_CTRL != nodeType(pNode)) {
6117
0
    return false;
6118
0
  }
6119
0
  SDynQueryCtrlPhysiNode* pDynCtrl = (SDynQueryCtrlPhysiNode*)pNode;
6120
0
  if (DYN_QTYPE_VTB_AGG != pDynCtrl->qType && DYN_QTYPE_VTB_INTERVAL != pDynCtrl->qType) {
6121
0
    return false;
6122
0
  }
6123
0
  if (nodesListGetNode(pDynCtrl->node.pChildren, 0)== NULL) {
6124
0
    return false;
6125
0
  }
6126
0
  return true;
6127
0
}
6128
6129
0
static int32_t getChildTuple(SDataBlockDescNode **pChildTuple, SNodeList* pChildren) {
6130
0
  if (isDynVirtualStableScan(nodesListGetNode(pChildren, 0))) {
6131
0
    SPhysiNode* pDynNode = (SPhysiNode*)nodesListGetNode(pChildren, 0);
6132
0
    if (pDynNode == NULL) {
6133
0
      planError("get child tuple failed, pDynNode is NULL, childrenNum:%d", pChildren->length);
6134
0
      return TSDB_CODE_INVALID_PARA;
6135
0
    }
6136
0
    SPhysiNode* pVtbScan = (SPhysiNode*)nodesListGetNode(pDynNode->pChildren, 0);
6137
0
    if (pVtbScan == NULL) {
6138
0
      planError("get child tuple failed, pVtbScan is NULL, dynNode childrenNum:%d", pDynNode->pChildren->length);
6139
0
      return TSDB_CODE_INVALID_PARA;
6140
0
    }
6141
0
    *pChildTuple = pVtbScan->pOutputDataBlockDesc;
6142
0
  } else if (isDynVirtualStableAgg(nodesListGetNode(pChildren, 0))) {
6143
0
    SPhysiNode* pDynNode = (SPhysiNode*)nodesListGetNode(pChildren, 0);
6144
0
    if (pDynNode == NULL) {
6145
0
      planError("get child tuple failed, pDynNode is NULL, childrenNum:%d", pChildren->length);
6146
0
      return TSDB_CODE_INVALID_PARA;
6147
0
    }
6148
0
    SPhysiNode* pAgg = (SPhysiNode*)nodesListGetNode(pDynNode->pChildren, LIST_LENGTH(pDynNode->pChildren) - 1);
6149
0
    if (pAgg == NULL) {
6150
0
      planError("get child tuple failed, pVtbScan is NULL, dynNode childrenNum:%d", pDynNode->pChildren->length);
6151
0
      return TSDB_CODE_INVALID_PARA;
6152
0
    }
6153
0
    *pChildTuple = pAgg->pOutputDataBlockDesc;
6154
0
  } else {
6155
0
    SPhysiNode* pChild = (SPhysiNode*)nodesListGetNode(pChildren, 0);
6156
0
    if (pChild == NULL) {
6157
0
      planError("get child tuple failed, pChild is NULL, childrenNum:%d", pChildren->length);
6158
0
      return TSDB_CODE_INVALID_PARA;
6159
0
    }
6160
0
    *pChildTuple = (((SPhysiNode*)nodesListGetNode(pChildren, 0))->pOutputDataBlockDesc);
6161
0
  }
6162
0
  return TSDB_CODE_SUCCESS;
6163
0
}
6164
6165
0
static bool forceNoMergeDataBlock(const SPhysiPlanContext* pCxt) {
6166
0
  return (pCxt != NULL && pCxt->pPlanCxt != NULL && pCxt->pPlanCxt->forceNoMergeDataBlock);
6167
0
}
6168
6169
static int32_t createAggPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren, SAggLogicNode* pAggLogicNode,
6170
0
                                  SPhysiNode** pPhyNode, SSubplan* pSubPlan) {
6171
0
  if (fqDiagEnabled()) {
6172
0
    fprintf(stderr,
6173
0
            "[FQ-DIAG] createAggPhysiNode logic=%p targets=%d groupKeys=%d aggFuncs=%d cond=%p childCnt=%d\n",
6174
0
            pAggLogicNode, pAggLogicNode->node.pTargets ? LIST_LENGTH(pAggLogicNode->node.pTargets) : 0,
6175
0
            pAggLogicNode->pGroupKeys ? LIST_LENGTH(pAggLogicNode->pGroupKeys) : 0,
6176
0
            pAggLogicNode->pAggFuncs ? LIST_LENGTH(pAggLogicNode->pAggFuncs) : 0,
6177
0
            pAggLogicNode->node.pConditions, pChildren ? LIST_LENGTH(pChildren) : 0);
6178
0
    fqDiagDumpExprList("agg.logic.targets", pAggLogicNode->node.pTargets);
6179
0
    fqDiagDumpExprList("agg.logic.groupKeys", pAggLogicNode->pGroupKeys);
6180
0
    fqDiagDumpExprList("agg.logic.aggFuncs", pAggLogicNode->pAggFuncs);
6181
0
  }
6182
0
  SAggPhysiNode* pAgg =
6183
0
      (SAggPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pAggLogicNode, QUERY_NODE_PHYSICAL_PLAN_HASH_AGG);
6184
0
  if (NULL == pAgg) {
6185
0
    return terrno;
6186
0
  }
6187
0
  if (pAgg->node.pSlimit && ((SLimitNode*)pAgg->node.pSlimit)->limit) {
6188
0
    pSubPlan->dynamicRowThreshold = true;
6189
0
    pSubPlan->rowsThreshold = ((SLimitNode*)pAgg->node.pSlimit)->limit->datum.i;
6190
0
  }
6191
6192
0
  pAgg->mergeDataBlock =
6193
0
      (forceNoMergeDataBlock(pCxt) || GROUP_ACTION_KEEP == pAggLogicNode->node.groupAction ? false : true);
6194
0
  pAgg->groupKeyOptimized = pAggLogicNode->hasGroupKeyOptimized;
6195
0
  pAgg->hasMixedDistinct = pAggLogicNode->hasMixedDistinct;
6196
0
  pAgg->numDistinctFuncs = pAggLogicNode->numDistinctFuncs;
6197
0
  pAgg->node.forceCreateNonBlockingOptr = pAggLogicNode->node.forceCreateNonBlockingOptr;
6198
6199
0
  SNodeList* pPrecalcExprs = NULL;
6200
0
  SNodeList* pGroupKeys = NULL;
6201
0
  SNodeList* pAggFuncs = NULL;
6202
0
  int32_t    code = rewritePrecalcExprs(pCxt, pAggLogicNode->pGroupKeys, &pPrecalcExprs, &pGroupKeys);
6203
0
  if (TSDB_CODE_SUCCESS == code) {
6204
0
    code = rewritePrecalcExprs(pCxt, pAggLogicNode->pAggFuncs, &pPrecalcExprs, &pAggFuncs);
6205
0
  }
6206
0
  nodesWalkExprs(pAggFuncs, hasCountLikeFunc, &pAgg->hasCountLikeFunc);
6207
6208
0
  SDataBlockDescNode* pChildTupe = NULL;
6209
0
  if (TSDB_CODE_SUCCESS == code) {
6210
0
    code = getChildTuple(&pChildTupe, pChildren);
6211
0
  }
6212
6213
  // push down expression to pOutputDataBlockDesc of child node
6214
0
  if (TSDB_CODE_SUCCESS == code && NULL != pPrecalcExprs) {
6215
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pPrecalcExprs, &pAgg->pExprs);
6216
0
    if (TSDB_CODE_SUCCESS == code) {
6217
0
      code = pushdownDataBlockSlots(pCxt, pAgg->pExprs, pChildTupe);
6218
0
    }
6219
    // For mixed distinct: also push precalc exprs to child[1]'s descriptor
6220
0
    if (TSDB_CODE_SUCCESS == code && pAggLogicNode->hasMixedDistinct && pChildren->length >= 2) {
6221
0
      SPhysiNode* pChild1 = (SPhysiNode*)nodesListGetNode(pChildren, 1);
6222
0
      if (pChild1 != NULL) {
6223
0
        code = pushdownDataBlockSlots(pCxt, pAgg->pExprs, pChild1->pOutputDataBlockDesc);
6224
0
      }
6225
0
    }
6226
0
  }
6227
6228
0
  if (TSDB_CODE_SUCCESS == code && NULL != pGroupKeys) {
6229
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pGroupKeys, &pAgg->pGroupKeys);
6230
0
    if (TSDB_CODE_SUCCESS == code) {
6231
0
      code = addDataBlockSlots(pCxt, pAgg->pGroupKeys, pAgg->node.pOutputDataBlockDesc);
6232
0
    }
6233
0
  }
6234
6235
0
  if (TSDB_CODE_SUCCESS == code && NULL != pAggFuncs) {
6236
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pAggFuncs, &pAgg->pAggFuncs);
6237
0
    if (TSDB_CODE_SUCCESS == code) {
6238
0
      code = addDataBlockSlots(pCxt, pAgg->pAggFuncs, pAgg->node.pOutputDataBlockDesc);
6239
0
    }
6240
0
  }
6241
6242
6243
0
  if (TSDB_CODE_SUCCESS == code) {
6244
0
    code = setConditionsSlotId(pCxt, (const SLogicNode*)pAggLogicNode, (SPhysiNode*)pAgg);
6245
0
  }
6246
6247
0
  if (TSDB_CODE_SUCCESS == code) {
6248
0
    *pPhyNode = (SPhysiNode*)pAgg;
6249
0
  } else {
6250
0
    nodesDestroyNode((SNode*)pAgg);
6251
0
  }
6252
6253
0
  nodesDestroyList(pPrecalcExprs);
6254
0
  nodesDestroyList(pGroupKeys);
6255
0
  nodesDestroyList(pAggFuncs);
6256
6257
0
  return code;
6258
0
}
6259
6260
static int32_t createIndefRowsFuncPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
6261
0
                                            SIndefRowsFuncLogicNode* pFuncLogicNode, SPhysiNode** pPhyNode) {
6262
0
  SIndefRowsFuncPhysiNode* pIdfRowsFunc = (SIndefRowsFuncPhysiNode*)makePhysiNode(
6263
0
      pCxt, (SLogicNode*)pFuncLogicNode, QUERY_NODE_PHYSICAL_PLAN_INDEF_ROWS_FUNC);
6264
0
  if (NULL == pIdfRowsFunc) {
6265
0
    return terrno;
6266
0
  }
6267
6268
0
  SNodeList* pPrecalcExprs = NULL;
6269
0
  SNodeList* pFuncs = NULL;
6270
0
  int32_t    code = rewritePrecalcExprs(pCxt, pFuncLogicNode->pFuncs, &pPrecalcExprs, &pFuncs);
6271
6272
0
  if (pIdfRowsFunc->node.inputTsOrder == 0) {
6273
    // default to asc
6274
0
    pIdfRowsFunc->node.inputTsOrder = TSDB_ORDER_ASC;
6275
0
  }
6276
6277
0
  SDataBlockDescNode* pChildTupe = NULL;
6278
0
  if (TSDB_CODE_SUCCESS == code) {
6279
0
    code = getChildTuple(&pChildTupe, pChildren);
6280
0
  }
6281
6282
  // push down expression to pOutputDataBlockDesc of child node
6283
0
  if (TSDB_CODE_SUCCESS == code && NULL != pPrecalcExprs) {
6284
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pPrecalcExprs, &pIdfRowsFunc->pExprs);
6285
0
    if (TSDB_CODE_SUCCESS == code) {
6286
0
      code = pushdownDataBlockSlots(pCxt, pIdfRowsFunc->pExprs, pChildTupe);
6287
0
    }
6288
0
  }
6289
6290
0
  if (TSDB_CODE_SUCCESS == code) {
6291
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pFuncs, &pIdfRowsFunc->pFuncs);
6292
0
    if (TSDB_CODE_SUCCESS == code) {
6293
0
      code = addDataBlockSlots(pCxt, pIdfRowsFunc->pFuncs, pIdfRowsFunc->node.pOutputDataBlockDesc);
6294
0
    }
6295
0
  }
6296
6297
0
  if (TSDB_CODE_SUCCESS == code) {
6298
0
    code = setConditionsSlotId(pCxt, (const SLogicNode*)pFuncLogicNode, (SPhysiNode*)pIdfRowsFunc);
6299
0
  }
6300
6301
0
  if (TSDB_CODE_SUCCESS == code) {
6302
0
    *pPhyNode = (SPhysiNode*)pIdfRowsFunc;
6303
0
  } else {
6304
0
    nodesDestroyNode((SNode*)pIdfRowsFunc);
6305
0
  }
6306
6307
0
  nodesDestroyList(pPrecalcExprs);
6308
0
  nodesDestroyList(pFuncs);
6309
6310
0
  return code;
6311
0
}
6312
6313
static int32_t createInterpFuncPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
6314
0
                                         SInterpFuncLogicNode* pFuncLogicNode, SPhysiNode** pPhyNode) {
6315
0
  SInterpFuncPhysiNode* pInterpFunc = (SInterpFuncPhysiNode*)makePhysiNode(
6316
0
      pCxt, (SLogicNode*)pFuncLogicNode, QUERY_NODE_PHYSICAL_PLAN_INTERP_FUNC);
6317
0
  if (NULL == pInterpFunc) {
6318
0
    return terrno;
6319
0
  }
6320
6321
0
  pInterpFunc->mergeDataBlock = !forceNoMergeDataBlock(pCxt) &&
6322
0
      GROUP_ACTION_KEEP != pFuncLogicNode->node.groupAction;
6323
6324
0
  SNodeList* pPrecalcExprs = NULL;
6325
0
  SNodeList* pFuncs = NULL;
6326
0
  int32_t    code = rewritePrecalcExprs(pCxt, pFuncLogicNode->pFuncs, &pPrecalcExprs, &pFuncs);
6327
6328
0
  SDataBlockDescNode* pChildTupe = NULL;
6329
0
  if (TSDB_CODE_SUCCESS == code) {
6330
0
    code = getChildTuple(&pChildTupe, pChildren);
6331
0
  }
6332
6333
  // push down expression to pOutputDataBlockDesc of child node
6334
0
  if (TSDB_CODE_SUCCESS == code && NULL != pPrecalcExprs) {
6335
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pPrecalcExprs, &pInterpFunc->pExprs);
6336
0
    if (TSDB_CODE_SUCCESS == code) {
6337
0
      code = pushdownDataBlockSlots(pCxt, pInterpFunc->pExprs, pChildTupe);
6338
0
    }
6339
0
  }
6340
6341
0
  if (TSDB_CODE_SUCCESS == code) {
6342
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pFuncs, &pInterpFunc->pFuncs);
6343
0
    if (TSDB_CODE_SUCCESS == code) {
6344
0
      code = addDataBlockSlots(pCxt, pInterpFunc->pFuncs, pInterpFunc->node.pOutputDataBlockDesc);
6345
0
    }
6346
0
  }
6347
6348
0
  if (TSDB_CODE_SUCCESS == code) {
6349
0
    pInterpFunc->timeRange = pFuncLogicNode->timeRange;
6350
0
    pInterpFunc->interval = pFuncLogicNode->interval;
6351
0
    pInterpFunc->fillMode = pFuncLogicNode->fillMode;
6352
0
    pInterpFunc->timelineSource = pFuncLogicNode->timelineSource;
6353
0
    pInterpFunc->interpMode = pFuncLogicNode->interpMode;
6354
0
    pInterpFunc->requireMainMembership =
6355
0
        pFuncLogicNode->requireMainMembership;
6356
0
    pInterpFunc->intervalUnit = pFuncLogicNode->intervalUnit;
6357
0
    pInterpFunc->precision = pFuncLogicNode->node.precision;
6358
0
    pInterpFunc->timezone = pFuncLogicNode->timezone;
6359
0
    tstrncpy(pInterpFunc->timezoneName, pFuncLogicNode->timezoneName,
6360
0
             sizeof(pInterpFunc->timezoneName));
6361
0
    pInterpFunc->pFillValues = NULL;
6362
0
    pInterpFunc->surroundingTime = pFuncLogicNode->surroundingTime;
6363
0
    code = nodesCloneNode(pFuncLogicNode->pTimeRange, &pInterpFunc->pTimeRange);
6364
0
  }
6365
  
6366
0
  if (TSDB_CODE_SUCCESS == code) {
6367
0
    code = nodesCloneNode(pFuncLogicNode->pFillValues, &pInterpFunc->pFillValues);
6368
0
  }
6369
6370
0
  if (TSDB_CODE_SUCCESS == code) {
6371
0
    code = setNodeSlotId(pCxt, pChildTupe->dataBlockId, -1, pFuncLogicNode->pTimeSeries, &pInterpFunc->pTimeSeries);
6372
0
  }
6373
6374
0
  if (TSDB_CODE_SUCCESS == code) {
6375
0
    code = setConditionsSlotId(pCxt, (const SLogicNode*)pFuncLogicNode, (SPhysiNode*)pInterpFunc);
6376
0
  }
6377
6378
0
  if (TSDB_CODE_SUCCESS == code) {
6379
0
    *pPhyNode = (SPhysiNode*)pInterpFunc;
6380
0
  } else {
6381
0
    nodesDestroyNode((SNode*)pInterpFunc);
6382
0
  }
6383
6384
0
  nodesDestroyList(pPrecalcExprs);
6385
0
  nodesDestroyList(pFuncs);
6386
6387
0
  return code;
6388
0
}
6389
6390
static int32_t createForecastFuncPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
6391
0
                                           SForecastFuncLogicNode* pFuncLogicNode, SPhysiNode** pPhyNode) {
6392
0
  SForecastFuncPhysiNode* pForecastFunc =
6393
0
      (SForecastFuncPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pFuncLogicNode, QUERY_NODE_PHYSICAL_PLAN_FORECAST_FUNC);
6394
0
  if (NULL == pForecastFunc) {
6395
0
    return terrno;
6396
0
  }
6397
6398
0
  SNodeList* pPrecalcExprs = NULL;
6399
0
  SNodeList* pFuncs = NULL;
6400
0
  int32_t    code = rewritePrecalcExprs(pCxt, pFuncLogicNode->pFuncs, &pPrecalcExprs, &pFuncs);
6401
6402
0
  SDataBlockDescNode* pChildTupe = NULL;
6403
0
  if (TSDB_CODE_SUCCESS == code) {
6404
0
    code = getChildTuple(&pChildTupe, pChildren);
6405
0
  }
6406
6407
  // push down expression to pOutputDataBlockDesc of child node
6408
0
  if (TSDB_CODE_SUCCESS == code && NULL != pPrecalcExprs) {
6409
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pPrecalcExprs, &pForecastFunc->pExprs);
6410
0
    if (TSDB_CODE_SUCCESS == code) {
6411
0
      code = pushdownDataBlockSlots(pCxt, pForecastFunc->pExprs, pChildTupe);
6412
0
    }
6413
0
  }
6414
6415
0
  if (TSDB_CODE_SUCCESS == code) {
6416
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pFuncs, &pForecastFunc->pFuncs);
6417
0
    if (TSDB_CODE_SUCCESS == code) {
6418
0
      code = addDataBlockSlots(pCxt, pForecastFunc->pFuncs, pForecastFunc->node.pOutputDataBlockDesc);
6419
0
    }
6420
0
  }
6421
6422
0
  if (TSDB_CODE_SUCCESS == code) {
6423
0
    code = setConditionsSlotId(pCxt, (const SLogicNode*)pFuncLogicNode, (SPhysiNode*)pForecastFunc);
6424
0
  }
6425
6426
0
  if (TSDB_CODE_SUCCESS == code) {
6427
0
    *pPhyNode = (SPhysiNode*)pForecastFunc;
6428
0
  } else {
6429
0
    nodesDestroyNode((SNode*)pForecastFunc);
6430
0
  }
6431
6432
0
  nodesDestroyList(pPrecalcExprs);
6433
0
  nodesDestroyList(pFuncs);
6434
6435
0
  return code;
6436
0
}
6437
6438
static int32_t createWindowFuncPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
6439
0
                                         SWindowFuncLogicNode* pWindowLogicNode, SPhysiNode** pPhyNode) {
6440
0
  SWindowFuncPhysiNode* pWindow =
6441
0
      (SWindowFuncPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pWindowLogicNode, QUERY_NODE_PHYSICAL_PLAN_WINDOW_FUNC);
6442
0
  if (NULL == pWindow) {
6443
0
    return terrno;
6444
0
  }
6445
6446
0
  SNodeList* pPrecalcExprs = NULL;
6447
0
  SNodeList* pFuncs = NULL;
6448
0
  SNodeList* pOrderKeys = NULL;
6449
0
  int32_t    code = rewritePrecalcExprs(pCxt, pWindowLogicNode->pFuncs, &pPrecalcExprs, &pFuncs);
6450
6451
0
  if (TSDB_CODE_SUCCESS == code) {
6452
0
    code = rewritePrecalcExprs(pCxt, pWindowLogicNode->pOrderKeys, &pPrecalcExprs, &pOrderKeys);
6453
0
  }
6454
6455
0
  SDataBlockDescNode* pChildTuple = NULL;
6456
0
  if (TSDB_CODE_SUCCESS == code) {
6457
0
    code = getChildTuple(&pChildTuple, pChildren);
6458
0
  }
6459
0
  if (TSDB_CODE_SUCCESS == code && NULL != pPrecalcExprs) {
6460
0
    code = setListSlotId(pCxt, pChildTuple->dataBlockId, -1, pPrecalcExprs, &pWindow->pExprs);
6461
0
    if (TSDB_CODE_SUCCESS == code) {
6462
0
      code = pushdownDataBlockSlots(pCxt, pWindow->pExprs, pChildTuple);
6463
0
    }
6464
0
  }
6465
0
  if (TSDB_CODE_SUCCESS == code) {
6466
0
    code =
6467
0
        setListSlotId(pCxt, pChildTuple->dataBlockId, -1, pWindowLogicNode->pPartitionKeys, &pWindow->pPartitionKeys);
6468
0
  }
6469
0
  if (TSDB_CODE_SUCCESS == code) {
6470
0
    code = setListSlotId(pCxt, pChildTuple->dataBlockId, -1, pOrderKeys, &pWindow->pOrderKeys);
6471
0
  }
6472
0
  if (TSDB_CODE_SUCCESS == code) {
6473
0
    code = setListSlotId(pCxt, pChildTuple->dataBlockId, -1, pFuncs, &pWindow->pFuncs);
6474
0
  }
6475
0
  if (TSDB_CODE_SUCCESS == code) {
6476
0
    code = nodesCloneNode(pWindowLogicNode->pFrame, &pWindow->pFrame);
6477
0
  }
6478
0
  if (TSDB_CODE_SUCCESS == code) {
6479
0
    code = addDataBlockSlots(pCxt, pWindow->pFuncs, pWindow->node.pOutputDataBlockDesc);
6480
0
  }
6481
0
  if (TSDB_CODE_SUCCESS == code) {
6482
0
    code = setConditionsSlotId(pCxt, (const SLogicNode*)pWindowLogicNode, (SPhysiNode*)pWindow);
6483
0
  }
6484
6485
0
  if (TSDB_CODE_SUCCESS == code) {
6486
0
    *pPhyNode = (SPhysiNode*)pWindow;
6487
0
  } else {
6488
0
    nodesDestroyNode((SNode*)pWindow);
6489
0
  }
6490
6491
0
  nodesDestroyList(pPrecalcExprs);
6492
0
  nodesDestroyList(pFuncs);
6493
0
  nodesDestroyList(pOrderKeys);
6494
6495
0
  return code;
6496
0
}
6497
6498
static int32_t createGenericAnalysisPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
6499
0
                                              SGenericAnalysisLogicNode* pFuncLogicNode, SPhysiNode** pPhyNode) {
6500
0
  SGenericAnalysisPhysiNode* pFunc = (SGenericAnalysisPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pFuncLogicNode,
6501
0
                                                                               QUERY_NODE_PHYSICAL_PLAN_ANALYSIS_FUNC);
6502
0
  if (NULL == pFunc) {
6503
0
    return terrno;
6504
0
  }
6505
6506
0
  SNodeList* pPrecalcExprs = NULL;
6507
0
  SNodeList* pFuncs = NULL;
6508
0
  int32_t    code = rewritePrecalcExprs(pCxt, pFuncLogicNode->pFuncs, &pPrecalcExprs, &pFuncs);
6509
6510
0
  SDataBlockDescNode* pChildTupe = (((SPhysiNode*)nodesListGetNode(pChildren, 0))->pOutputDataBlockDesc);
6511
  // push down expression to pOutputDataBlockDesc of child node
6512
0
  if (TSDB_CODE_SUCCESS == code && NULL != pPrecalcExprs) {
6513
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pPrecalcExprs, &pFunc->pExprs);
6514
0
    if (TSDB_CODE_SUCCESS == code) {
6515
0
      code = pushdownDataBlockSlots(pCxt, pFunc->pExprs, pChildTupe);
6516
0
    }
6517
0
  }
6518
6519
0
  if (TSDB_CODE_SUCCESS == code) {
6520
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pFuncs, &pFunc->pFuncs);
6521
0
    if (TSDB_CODE_SUCCESS == code) {
6522
0
      code = addDataBlockSlots(pCxt, pFunc->pFuncs, pFunc->node.pOutputDataBlockDesc);
6523
0
    }
6524
0
  }
6525
6526
0
  if (TSDB_CODE_SUCCESS == code) {
6527
0
    code = setConditionsSlotId(pCxt, (const SLogicNode*)pFuncLogicNode, (SPhysiNode*)pFunc);
6528
0
  }
6529
6530
0
  if (TSDB_CODE_SUCCESS == code) {
6531
0
    *pPhyNode = (SPhysiNode*)pFunc;
6532
0
  } else {
6533
0
    nodesDestroyNode((SNode*)pFunc);
6534
0
  }
6535
6536
0
  nodesDestroyList(pPrecalcExprs);
6537
0
  nodesDestroyList(pFuncs);
6538
6539
0
  return code;
6540
0
}
6541
6542
static int32_t createRowsetSourcePhysiNode(SPhysiPlanContext* pCxt, SRowsetSourceLogicNode* pLogicNode,
6543
0
                                           SPhysiNode** pPhyNode) {
6544
0
  SRowsetSourcePhysiNode* pRowset =
6545
0
      (SRowsetSourcePhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pLogicNode, QUERY_NODE_PHYSICAL_PLAN_ROWSET_SOURCE);
6546
0
  if (NULL == pRowset) {
6547
0
    return terrno;
6548
0
  }
6549
6550
0
  pRowset->colCount      = pLogicNode->colCount;
6551
0
  pRowset->numBlocks     = pLogicNode->numBlocks;
6552
0
  pRowset->totalRows     = pLogicNode->totalRows;
6553
0
  pRowset->hasPrimaryTs  = pLogicNode->hasPrimaryTs;
6554
0
  pRowset->isSortedByTs  = pLogicNode->isSortedByTs;
6555
0
  pRowset->primaryTsSlot = pLogicNode->primaryTsSlot;
6556
0
  pRowset->blockBufLen   = pLogicNode->blockBufLen;
6557
0
  pRowset->pBlockBuf     = pLogicNode->pBlockBuf;
6558
0
  pLogicNode->pBlockBuf  = NULL;  // ownership transferred
6559
6560
0
  int32_t code = setConditionsSlotId(pCxt, (const SLogicNode*)pLogicNode, (SPhysiNode*)pRowset);
6561
0
  if (TSDB_CODE_SUCCESS != code) {
6562
0
    nodesDestroyNode((SNode*)pRowset);
6563
0
    return code;
6564
0
  }
6565
6566
0
  *pPhyNode = (SPhysiNode*)pRowset;
6567
0
  return TSDB_CODE_SUCCESS;
6568
0
}
6569
6570
0
static bool projectCanMergeDataBlock(SProjectLogicNode* pProject, bool topLevelSubplan) {
6571
  // Split/scale-out may reset logic-parent pointers at subplan boundaries.
6572
  // Allow merge only when Project is the root output of the top-level subplan.
6573
0
  if (!topLevelSubplan || pProject->node.pParent != NULL) {
6574
0
    return false;
6575
0
  }
6576
6577
0
  if (GROUP_ACTION_KEEP == pProject->node.groupAction) {
6578
0
    return false;
6579
0
  }
6580
0
  if (DATA_ORDER_LEVEL_NONE == pProject->node.resultDataOrder) {
6581
0
    return true;
6582
0
  }
6583
0
  if (1 != LIST_LENGTH(pProject->node.pChildren)) {
6584
0
    return true;
6585
0
  }
6586
0
  SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pProject->node.pChildren, 0);
6587
0
  return DATA_ORDER_LEVEL_GLOBAL == pChild->resultDataOrder ? true : false;
6588
0
}
6589
6590
0
bool projectCouldMergeUnsortDataBlock(SProjectLogicNode* pProject) {
6591
0
  SLogicNode* pChild = (SLogicNode*)nodesListGetNode(pProject->node.pChildren, 0);
6592
0
  if (DATA_ORDER_LEVEL_GLOBAL == pChild->resultDataOrder) {
6593
0
    return false;
6594
0
  }
6595
0
  if (GROUP_ACTION_KEEP == pProject->node.groupAction) {
6596
0
    return false;
6597
0
  }
6598
0
  if (DATA_ORDER_LEVEL_NONE == pProject->node.resultDataOrder) {
6599
0
    return true;
6600
0
  }
6601
0
  if (1 != LIST_LENGTH(pProject->node.pChildren)) {
6602
0
    return true;
6603
0
  }
6604
0
  return false;
6605
0
}
6606
6607
static int32_t createProjectPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
6608
                                      SProjectLogicNode* pProjectLogicNode, SSubplan* pSubplan,
6609
0
                                      SPhysiNode** pPhyNode) {
6610
0
  SProjectPhysiNode* pProject =
6611
0
      (SProjectPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pProjectLogicNode, QUERY_NODE_PHYSICAL_PLAN_PROJECT);
6612
0
  if (NULL == pProject) {
6613
0
    return terrno;
6614
0
  }
6615
6616
0
  pProject->mergeDataBlock =
6617
0
      (forceNoMergeDataBlock(pCxt) ? false : projectCanMergeDataBlock(pProjectLogicNode, (0 == pSubplan->level)));
6618
0
  pProject->ignoreGroupId = pProjectLogicNode->ignoreGroupId;
6619
0
  pProject->inputIgnoreGroup = pProjectLogicNode->inputIgnoreGroup;
6620
6621
0
  int32_t code = TSDB_CODE_SUCCESS;
6622
0
  if (0 == LIST_LENGTH(pChildren)) {
6623
0
    code = nodesCloneList(pProjectLogicNode->pProjections, &pProject->pProjections);
6624
0
  } else {
6625
0
    code = setListSlotId(pCxt, ((SPhysiNode*)nodesListGetNode(pChildren, 0))->pOutputDataBlockDesc->dataBlockId, -1,
6626
0
                         pProjectLogicNode->pProjections, &pProject->pProjections);
6627
0
  }
6628
0
  if (TSDB_CODE_SUCCESS == code) {
6629
0
    code = addDataBlockSlotsForProject(pCxt, pProjectLogicNode->stmtName, pProject->pProjections,
6630
0
                                       pProject->node.pOutputDataBlockDesc);
6631
0
  }
6632
0
  if (TSDB_CODE_SUCCESS == code) {
6633
0
    code = setConditionsSlotId(pCxt, (const SLogicNode*)pProjectLogicNode, (SPhysiNode*)pProject);
6634
0
  }
6635
6636
0
  if (TSDB_CODE_SUCCESS == code) {
6637
0
    *pPhyNode = (SPhysiNode*)pProject;
6638
0
  } else {
6639
0
    nodesDestroyNode((SNode*)pProject);
6640
0
  }
6641
6642
0
  return code;
6643
0
}
6644
6645
static int32_t doCreateExchangePhysiNode(SPhysiPlanContext* pCxt, SExchangeLogicNode* pExchangeLogicNode,
6646
0
                                         SPhysiNode** pPhyNode) {
6647
0
  SExchangePhysiNode* pExchange =
6648
0
      (SExchangePhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pExchangeLogicNode, QUERY_NODE_PHYSICAL_PLAN_EXCHANGE);
6649
0
  if (NULL == pExchange) {
6650
0
    return terrno;
6651
0
  }
6652
6653
0
  pExchange->srcStartGroupId = pExchangeLogicNode->srcStartGroupId;
6654
0
  pExchange->srcEndGroupId = pExchangeLogicNode->srcEndGroupId;
6655
0
  pExchange->seqRecvData = pExchangeLogicNode->seqRecvData;
6656
0
  pExchange->dynTbname = pExchangeLogicNode->dynTbname;
6657
0
  pExchange->fastInterval = pExchangeLogicNode->fastInterval;
6658
0
  pExchange->localBlockExchange = pExchangeLogicNode->localBlockExchange;
6659
6660
0
  int32_t code = setConditionsSlotId(pCxt, (const SLogicNode*)pExchangeLogicNode, (SPhysiNode*)pExchange);
6661
0
  if (TSDB_CODE_SUCCESS == code) {
6662
0
    *pPhyNode = (SPhysiNode*)pExchange;
6663
0
  } else {
6664
0
    nodesDestroyNode((SNode*)pExchange);
6665
0
  }
6666
6667
0
  return code;
6668
0
}
6669
6670
static int32_t createExchangePhysiNode(SPhysiPlanContext* pCxt, SExchangeLogicNode* pExchangeLogicNode,
6671
0
                                       SPhysiNode** pPhyNode) {
6672
0
  return doCreateExchangePhysiNode(pCxt, pExchangeLogicNode, pPhyNode);
6673
0
}
6674
6675
static int32_t createWindowPhysiNodeFinalize(SPhysiPlanContext* pCxt, SNodeList* pChildren, SWindowPhysiNode* pWindow,
6676
0
                                             SWindowLogicNode* pWindowLogicNode) {
6677
0
  pWindow->indefRowsFunc = pWindowLogicNode->indefRowsFunc;
6678
0
  pWindow->mergeDataBlock =
6679
0
      (forceNoMergeDataBlock(pCxt) || GROUP_ACTION_KEEP == pWindowLogicNode->node.groupAction ? false : true);
6680
0
  pWindow->node.inputTsOrder = pWindowLogicNode->node.inputTsOrder;
6681
0
  pWindow->node.outputTsOrder = pWindowLogicNode->node.outputTsOrder;
6682
0
  if (nodeType(pWindow) == QUERY_NODE_PHYSICAL_PLAN_MERGE_ALIGNED_INTERVAL) {
6683
0
    pWindow->node.inputTsOrder = pWindowLogicNode->node.outputTsOrder;
6684
0
  }
6685
6686
0
  SNodeList* pPrecalcExprs = NULL;
6687
0
  SNodeList* pFuncs = NULL;
6688
0
  SNodeList* pProjs = pWindowLogicNode->pProjs;
6689
0
  int32_t    code = rewritePrecalcExprs(pCxt, pWindowLogicNode->pFuncs, &pPrecalcExprs, &pFuncs);
6690
6691
0
  SDataBlockDescNode* pChildTupe = NULL;
6692
0
  if (TSDB_CODE_SUCCESS == code) {
6693
0
    code = getChildTuple(&pChildTupe, pChildren);
6694
0
  }
6695
  // push down expression to pOutputDataBlockDesc of child node
6696
0
  if (TSDB_CODE_SUCCESS == code && NULL != pPrecalcExprs) {
6697
0
    SNodeList* pOutput;
6698
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pPrecalcExprs, &pOutput);
6699
0
    if (TSDB_CODE_SUCCESS == code) {
6700
0
      code = addDataBlockSlots(pCxt, pOutput, pChildTupe);
6701
0
    }
6702
0
    if (TSDB_CODE_SUCCESS == code) {
6703
0
      if (pWindow->pExprs == NULL) {
6704
0
        pWindow->pExprs = pOutput;
6705
0
      } else {
6706
0
        code = nodesListAppendList(pWindow->pExprs, pOutput);
6707
0
      }
6708
0
    }
6709
0
  }
6710
6711
0
  if (TSDB_CODE_SUCCESS == code) {
6712
0
    code = setNodeSlotId(pCxt, pChildTupe->dataBlockId, -1, pWindowLogicNode->pTspk, &pWindow->pTspk);
6713
0
  }
6714
0
  if (TSDB_CODE_SUCCESS == code && pWindowLogicNode->pTsEnd) {
6715
0
    code = setNodeSlotId(pCxt, pChildTupe->dataBlockId, -1, pWindowLogicNode->pTsEnd, &pWindow->pTsEnd);
6716
0
  }
6717
6718
0
  if (TSDB_CODE_SUCCESS == code && NULL != pFuncs) {
6719
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pFuncs, &pWindow->pFuncs);
6720
0
    if (TSDB_CODE_SUCCESS == code) {
6721
0
      code = addDataBlockSlots(pCxt, pWindow->pFuncs, pWindow->node.pOutputDataBlockDesc);
6722
0
    }
6723
0
  }
6724
6725
0
  if (TSDB_CODE_SUCCESS == code && NULL != pProjs) {
6726
    // For the agg + fill case, pProjs contains expressions like sum(v)_col_ref + 1
6727
    // where sum(v)_col_ref was substituted by the logic-layer rewrite.  The agg result
6728
    // slot (sum(v)) lives in the ExtWin output block, which was populated by
6729
    // addDataBlockSlots(pFuncs) above.  Use that block as the primary lookup target
6730
    // so the col_ref resolves to the correct slot.  Fall back to the child block for
6731
    // any other column refs (e.g. _wstart pseudo-column on the child side).
6732
0
    int64_t projPrimaryBlockId = (NULL != pFuncs)
6733
0
                                     ? pWindow->node.pOutputDataBlockDesc->dataBlockId
6734
0
                                     : pChildTupe->dataBlockId;
6735
0
    int64_t projSecondaryBlockId = (NULL != pFuncs) ? pChildTupe->dataBlockId : -1;
6736
0
    code = setListSlotId(pCxt, projPrimaryBlockId, projSecondaryBlockId, pProjs, &pWindow->pProjs);
6737
0
    if (TSDB_CODE_SUCCESS == code) {
6738
0
      code = addDataBlockSlots(pCxt, pWindow->pProjs, pWindow->node.pOutputDataBlockDesc);
6739
0
    }
6740
0
  }
6741
6742
0
  if (TSDB_CODE_SUCCESS == code) {
6743
0
    code = setConditionsSlotId(pCxt, (const SLogicNode*)pWindowLogicNode, (SPhysiNode*)pWindow);
6744
0
  }
6745
6746
0
  nodesDestroyList(pPrecalcExprs);
6747
0
  nodesDestroyList(pFuncs);
6748
6749
0
  return code;
6750
0
}
6751
6752
0
static ENodeType getIntervalOperatorType(EWindowAlgorithm windowAlgo) {
6753
0
  switch (windowAlgo) {
6754
0
    case INTERVAL_ALGO_HASH:
6755
0
      return QUERY_NODE_PHYSICAL_PLAN_HASH_INTERVAL;
6756
0
    case INTERVAL_ALGO_MERGE:
6757
0
      return QUERY_NODE_PHYSICAL_PLAN_MERGE_ALIGNED_INTERVAL;
6758
0
    default:
6759
0
      break;
6760
0
  }
6761
0
  return QUERY_NODE_PHYSICAL_PLAN_HASH_INTERVAL;
6762
0
}
6763
6764
0
static ENodeType getExternalOperatorType(EWindowAlgorithm windowAlgo) {
6765
0
  switch (windowAlgo) {
6766
0
    case EXTERNAL_ALGO_HASH:
6767
0
      return QUERY_NODE_PHYSICAL_PLAN_HASH_EXTERNAL;
6768
0
    case EXTERNAL_ALGO_MERGE:
6769
0
      return QUERY_NODE_PHYSICAL_PLAN_MERGE_ALIGNED_EXTERNAL;
6770
0
    default:
6771
0
      break;
6772
0
  }
6773
0
  return QUERY_NODE_PHYSICAL_PLAN_HASH_EXTERNAL;
6774
0
}
6775
6776
static int32_t createIntervalPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
6777
0
                                       SWindowLogicNode* pWindowLogicNode, SPhysiNode** pPhyNode) {
6778
0
  SIntervalPhysiNode* pInterval = (SIntervalPhysiNode*)makePhysiNode(
6779
0
      pCxt, (SLogicNode*)pWindowLogicNode, getIntervalOperatorType(pWindowLogicNode->windowAlgo));
6780
0
  if (NULL == pInterval) {
6781
0
    return terrno;
6782
0
  }
6783
6784
0
  pInterval->interval = pWindowLogicNode->interval;
6785
0
  pInterval->offset = pWindowLogicNode->offset;
6786
0
  pInterval->sliding = pWindowLogicNode->sliding;
6787
0
  pInterval->intervalUnit = pWindowLogicNode->intervalUnit;
6788
0
  pInterval->slidingUnit = pWindowLogicNode->slidingUnit;
6789
0
  pInterval->timeRange = pWindowLogicNode->timeRange;
6790
0
  pInterval->firstDayOfWeek = pWindowLogicNode->firstDayOfWeek;
6791
0
  pInterval->fastInterval = pWindowLogicNode->fastInterval;
6792
0
  pInterval->exInterval = pWindowLogicNode->exInterval;
6793
0
  pInterval->timezone = pWindowLogicNode->timezone;
6794
0
  tstrncpy(pInterval->timezoneName, pWindowLogicNode->timezoneName, sizeof(pInterval->timezoneName));
6795
0
  int32_t code = createWindowPhysiNodeFinalize(pCxt, pChildren, &pInterval->window, pWindowLogicNode);
6796
0
  if (TSDB_CODE_SUCCESS == code) {
6797
0
    *pPhyNode = (SPhysiNode*)pInterval;
6798
0
  } else {
6799
0
    nodesDestroyNode((SNode*)pInterval);
6800
0
  }
6801
6802
0
  return code;
6803
0
}
6804
6805
static int32_t createSessionWindowPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
6806
0
                                            SWindowLogicNode* pWindowLogicNode, SPhysiNode** pPhyNode) {
6807
0
  SSessionWinodwPhysiNode* pSession = (SSessionWinodwPhysiNode*)makePhysiNode(
6808
0
      pCxt, (SLogicNode*)pWindowLogicNode, QUERY_NODE_PHYSICAL_PLAN_MERGE_SESSION);
6809
0
  if (NULL == pSession) {
6810
0
    return terrno;
6811
0
  }
6812
6813
0
  pSession->gap = pWindowLogicNode->sessionGap;
6814
6815
0
  int32_t code = createWindowPhysiNodeFinalize(pCxt, pChildren, &pSession->window, pWindowLogicNode);
6816
0
  if (TSDB_CODE_SUCCESS == code) {
6817
0
    *pPhyNode = (SPhysiNode*)pSession;
6818
0
  } else {
6819
0
    nodesDestroyNode((SNode*)pSession);
6820
0
  }
6821
6822
0
  return code;
6823
0
}
6824
6825
static int32_t createStateWindowPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
6826
0
                                          SWindowLogicNode* pWindowLogicNode, SPhysiNode** pPhyNode) {
6827
0
  ENodeType type = QUERY_NODE_PHYSICAL_PLAN_MERGE_STATE;
6828
0
  SStateWindowPhysiNode* pState = (SStateWindowPhysiNode*)makePhysiNode(
6829
0
      pCxt, (SLogicNode*)pWindowLogicNode, type);
6830
0
  if (NULL == pState) {
6831
0
    return terrno;
6832
0
  }
6833
6834
0
  SNodeList* pPrecalcExprs = NULL;
6835
0
  SNodeList* pStateKeys = NULL;
6836
0
  int32_t    code = rewritePrecalcExprs(pCxt, pWindowLogicNode->pStateExprs, &pPrecalcExprs, &pStateKeys);
6837
6838
0
  SDataBlockDescNode* pChildTupe = NULL;
6839
0
  if (TSDB_CODE_SUCCESS == code) {
6840
0
    code = getChildTuple(&pChildTupe, pChildren);
6841
0
  }
6842
  // push down expression to pOutputDataBlockDesc of child node
6843
0
  if (TSDB_CODE_SUCCESS == code && NULL != pPrecalcExprs) {
6844
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pPrecalcExprs, &pState->window.pExprs);
6845
0
    if (TSDB_CODE_SUCCESS == code) {
6846
0
      code = addDataBlockSlots(pCxt, pState->window.pExprs, pChildTupe);
6847
0
    }
6848
0
  }
6849
6850
0
  if (TSDB_CODE_SUCCESS == code) {
6851
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pStateKeys, &pState->pStateKeys);
6852
0
  }
6853
6854
0
  pState->extendOption = pWindowLogicNode->extendOption;
6855
0
  pState->trueForType = pWindowLogicNode->trueForType;
6856
0
  pState->trueForCount = pWindowLogicNode->trueForCount;
6857
0
  pState->trueForDuration = pWindowLogicNode->trueForDuration;
6858
6859
0
  if (TSDB_CODE_SUCCESS == code) {
6860
0
    code = createWindowPhysiNodeFinalize(pCxt, pChildren, &pState->window, pWindowLogicNode);
6861
0
  }
6862
6863
0
  if (TSDB_CODE_SUCCESS == code) {
6864
0
    *pPhyNode = (SPhysiNode*)pState;
6865
0
  } else {
6866
0
    nodesDestroyNode((SNode*)pState);
6867
0
  }
6868
6869
0
  nodesDestroyList(pPrecalcExprs);
6870
0
  nodesDestroyList(pStateKeys);
6871
6872
0
  return code;
6873
0
}
6874
6875
static int32_t createEventWindowPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
6876
0
                                          SWindowLogicNode* pWindowLogicNode, SPhysiNode** pPhyNode) {
6877
0
  ENodeType type = QUERY_NODE_PHYSICAL_PLAN_MERGE_EVENT;
6878
6879
0
  SEventWinodwPhysiNode* pEvent = (SEventWinodwPhysiNode*)makePhysiNode(
6880
0
      pCxt, (SLogicNode*)pWindowLogicNode, type);
6881
0
  if (NULL == pEvent) {
6882
0
    return terrno;
6883
0
  }
6884
0
  int32_t             code = TSDB_CODE_SUCCESS;
6885
0
  SDataBlockDescNode* pChildTupe = NULL;
6886
0
  if (TSDB_CODE_SUCCESS == code) {
6887
0
    code = getChildTuple(&pChildTupe, pChildren);
6888
0
  }
6889
0
  if (TSDB_CODE_SUCCESS == code) {
6890
0
    code = setNodeSlotId(pCxt, pChildTupe->dataBlockId, -1, pWindowLogicNode->pStartCond, &pEvent->pStartCond);
6891
0
  }
6892
0
  if (TSDB_CODE_SUCCESS == code) {
6893
0
    code = setNodeSlotId(pCxt, pChildTupe->dataBlockId, -1, pWindowLogicNode->pEndCond, &pEvent->pEndCond);
6894
0
  }
6895
0
  pEvent->trueForType = pWindowLogicNode->trueForType;
6896
0
  pEvent->trueForCount = pWindowLogicNode->trueForCount;
6897
0
  pEvent->trueForDuration = pWindowLogicNode->trueForDuration;
6898
0
  pEvent->startTrueForType     = pWindowLogicNode->startTrueForType;
6899
0
  pEvent->startTrueForCount    = pWindowLogicNode->startTrueForCount;
6900
0
  pEvent->startTrueForDuration = pWindowLogicNode->startTrueForDuration;
6901
0
  pEvent->endTrueForType       = pWindowLogicNode->endTrueForType;
6902
0
  pEvent->endTrueForCount      = pWindowLogicNode->endTrueForCount;
6903
0
  pEvent->endTrueForDuration   = pWindowLogicNode->endTrueForDuration;
6904
0
  if (TSDB_CODE_SUCCESS == code) {
6905
0
    code = createWindowPhysiNodeFinalize(pCxt, pChildren, &pEvent->window, pWindowLogicNode);
6906
0
  }
6907
6908
0
  if (TSDB_CODE_SUCCESS == code) {
6909
0
    *pPhyNode = (SPhysiNode*)pEvent;
6910
0
  } else {
6911
0
    nodesDestroyNode((SNode*)pEvent);
6912
0
  }
6913
6914
0
  return code;
6915
0
}
6916
6917
static int32_t createCountWindowPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
6918
0
                                          SWindowLogicNode* pWindowLogicNode, SPhysiNode** pPhyNode) {
6919
0
  ENodeType type = QUERY_NODE_PHYSICAL_PLAN_MERGE_COUNT;
6920
6921
0
  SCountWindowPhysiNode* pCount = (SCountWindowPhysiNode*)makePhysiNode(
6922
0
      pCxt, (SLogicNode*)pWindowLogicNode, type);
6923
0
  if (NULL == pCount) {
6924
0
    return terrno;
6925
0
  }
6926
0
  pCount->windowCount = pWindowLogicNode->windowCount;
6927
0
  pCount->windowSliding = pWindowLogicNode->windowSliding;
6928
6929
0
  int32_t code = createWindowPhysiNodeFinalize(pCxt, pChildren, &pCount->window, pWindowLogicNode);
6930
0
  if (TSDB_CODE_SUCCESS == code) {
6931
0
    *pPhyNode = (SPhysiNode*)pCount;
6932
0
  } else {
6933
0
    nodesDestroyNode((SNode*)pCount);
6934
0
  }
6935
6936
0
  return code;
6937
0
}
6938
6939
static int32_t createAnomalyWindowPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
6940
0
                                            SWindowLogicNode* pWindowLogicNode, SPhysiNode** pPhyNode) {
6941
0
  SAnomalyWindowPhysiNode* pAnomaly = (SAnomalyWindowPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pWindowLogicNode,
6942
0
                                                                              QUERY_NODE_PHYSICAL_PLAN_MERGE_ANOMALY);
6943
0
  if (NULL == pAnomaly) {
6944
0
    return terrno;
6945
0
  }
6946
6947
0
  SNodeList*          pPrecalcExprs = NULL;
6948
0
  SNodeList*          pAnomalyKeys = NULL;
6949
0
  SDataBlockDescNode* pChildTupe = NULL;
6950
6951
0
  int32_t code = rewritePrecalcExprs(pCxt, pWindowLogicNode->pAnomalyExpr, &pPrecalcExprs, &pAnomalyKeys);
6952
0
  if (TSDB_CODE_SUCCESS == code) {
6953
0
    code = getChildTuple(&pChildTupe, pChildren);
6954
0
  }
6955
6956
  // push down expression to pOutputDataBlockDesc of child node
6957
0
  if (TSDB_CODE_SUCCESS == code && NULL != pPrecalcExprs) {
6958
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pPrecalcExprs, &pAnomaly->window.pExprs);
6959
0
    if (TSDB_CODE_SUCCESS == code) {
6960
0
      code = addDataBlockSlots(pCxt, pAnomaly->window.pExprs, pChildTupe);
6961
0
    }
6962
0
  }
6963
6964
0
  if (TSDB_CODE_SUCCESS == code) {
6965
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pAnomalyKeys, &pAnomaly->pAnomalyKeys);
6966
0
    if (TSDB_CODE_SUCCESS == code) {
6967
      // code = addDataBlockSlots(pCxt, pAnomaly->pAnomalyKeys, pAnomaly->window.node.pOutputDataBlockDesc);
6968
0
    }
6969
0
  }
6970
6971
0
  tstrncpy(pAnomaly->anomalyOpt, pWindowLogicNode->anomalyOpt, sizeof(pAnomaly->anomalyOpt));
6972
6973
0
  if (TSDB_CODE_SUCCESS == code) {
6974
0
    code = createWindowPhysiNodeFinalize(pCxt, pChildren, &pAnomaly->window, pWindowLogicNode);
6975
0
  }
6976
6977
0
  if (TSDB_CODE_SUCCESS == code) {
6978
0
    *pPhyNode = (SPhysiNode*)pAnomaly;
6979
0
  } else {
6980
0
    nodesDestroyNode((SNode*)pAnomaly);
6981
0
  }
6982
6983
0
  nodesDestroyList(pPrecalcExprs);
6984
0
  nodesDestroyList(pAnomalyKeys);
6985
6986
0
  return code;
6987
0
}
6988
6989
0
static const char* getExternalWindowExprOutputName(SNode* pNode) {
6990
0
  if (pNode == NULL) {
6991
0
    return NULL;
6992
0
  }
6993
6994
0
  if (QUERY_NODE_TARGET == nodeType(pNode)) {
6995
0
    pNode = ((STargetNode*)pNode)->pExpr;
6996
0
    if (pNode == NULL) {
6997
0
      return NULL;
6998
0
    }
6999
0
  }
7000
7001
0
  if ('\0' != ((SExprNode*)pNode)->aliasName[0]) {
7002
0
    return ((SExprNode*)pNode)->aliasName;
7003
0
  }
7004
7005
0
  if (QUERY_NODE_COLUMN == nodeType(pNode) && '\0' != ((SColumnNode*)pNode)->colName[0]) {
7006
0
    return ((SColumnNode*)pNode)->colName;
7007
0
  }
7008
7009
0
  return NULL;
7010
0
}
7011
7012
static int32_t createExternalWindowOutputColumn(const SExternalWindowPhysiNode* pExternal, int32_t targetIdx,
7013
0
                                                SNode** pOutput) {
7014
0
  if (pExternal == NULL || pOutput == NULL || pExternal->window.node.pOutputDataBlockDesc == NULL) {
7015
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
7016
0
  }
7017
7018
0
  SSlotDescNode* pSlotDesc =
7019
0
      (SSlotDescNode*)nodesListGetNode(pExternal->window.node.pOutputDataBlockDesc->pSlots, targetIdx);
7020
0
  if (pSlotDesc == NULL) {
7021
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
7022
0
  }
7023
7024
0
  SColumnNode* pCol = NULL;
7025
0
  int32_t      code = nodesMakeNode(QUERY_NODE_COLUMN, (SNode**)&pCol);
7026
0
  if (NULL == pCol) {
7027
0
    return code;
7028
0
  }
7029
7030
0
  pCol->dataBlockId = pExternal->window.node.pOutputDataBlockDesc->dataBlockId;
7031
0
  pCol->slotId = pSlotDesc->slotId;
7032
0
  pCol->colType = COLUMN_TYPE_COLUMN;
7033
0
  pCol->node.resType = pSlotDesc->dataType;
7034
0
  tstrncpy(pCol->colName, pSlotDesc->name, TSDB_COL_NAME_LEN);
7035
0
  tstrncpy(pCol->node.aliasName, pSlotDesc->name, TSDB_COL_NAME_LEN);
7036
0
  tstrncpy(pCol->node.userAlias, pSlotDesc->name, TSDB_COL_NAME_LEN);
7037
7038
0
  *pOutput = (SNode*)pCol;
7039
0
  return TSDB_CODE_SUCCESS;
7040
0
}
7041
7042
// Build a fill output column using the TARGET node from pProjs (after addDataBlockSlots has run).
7043
// The TARGET node's slotId is the correct OPERATOR slot, not the phantom slot from makePhysiNode.
7044
static int32_t createExternalWindowOutputColumnByProjIdx(const SExternalWindowPhysiNode* pExternal,
7045
0
                                                         int32_t projIdx, SNode** pOutput) {
7046
0
  if (pExternal == NULL || pOutput == NULL || pExternal->window.node.pOutputDataBlockDesc == NULL) {
7047
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
7048
0
  }
7049
7050
0
  STargetNode* pTargetNode =
7051
0
      (STargetNode*)nodesListGetNode(pExternal->window.pProjs, projIdx);
7052
0
  if (pTargetNode == NULL || QUERY_NODE_TARGET != nodeType(pTargetNode)) {
7053
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
7054
0
  }
7055
7056
  // Find the SlotDescNode that matches this TARGET's slotId.
7057
0
  SSlotDescNode* pSlotDesc = NULL;
7058
0
  SNode*         pSlotNode = NULL;
7059
0
  FOREACH(pSlotNode, pExternal->window.node.pOutputDataBlockDesc->pSlots) {
7060
0
    SSlotDescNode* pSlot = (SSlotDescNode*)pSlotNode;
7061
0
    if (pSlot->slotId == pTargetNode->slotId) {
7062
0
      pSlotDesc = pSlot;
7063
0
      break;
7064
0
    }
7065
0
  }
7066
0
  if (pSlotDesc == NULL) {
7067
0
    return TSDB_CODE_PLAN_INTERNAL_ERROR;
7068
0
  }
7069
7070
0
  SColumnNode* pCol = NULL;
7071
0
  int32_t      code = nodesMakeNode(QUERY_NODE_COLUMN, (SNode**)&pCol);
7072
0
  if (NULL == pCol) {
7073
0
    return code;
7074
0
  }
7075
7076
0
  pCol->dataBlockId = pExternal->window.node.pOutputDataBlockDesc->dataBlockId;
7077
0
  pCol->slotId = pSlotDesc->slotId;
7078
0
  pCol->colType = COLUMN_TYPE_COLUMN;
7079
0
  pCol->node.resType = pSlotDesc->dataType;
7080
0
  tstrncpy(pCol->colName, pSlotDesc->name, TSDB_COL_NAME_LEN);
7081
0
  tstrncpy(pCol->node.aliasName, pSlotDesc->name, TSDB_COL_NAME_LEN);
7082
0
  tstrncpy(pCol->node.userAlias, pSlotDesc->name, TSDB_COL_NAME_LEN);
7083
7084
0
  *pOutput = (SNode*)pCol;
7085
0
  return TSDB_CODE_SUCCESS;
7086
0
}
7087
7088
0
static int32_t getExternalWindowOutputIndexByExpr(const SWindowLogicNode* pWindowLogicNode, SNode* pFillExpr) {
7089
0
  if (LIST_LENGTH(pWindowLogicNode->node.pTargets) == LIST_LENGTH(pWindowLogicNode->pProjs)) {
7090
0
    int32_t targetIdx = 0;
7091
0
    SNode*  pTarget = NULL;
7092
7093
0
    FOREACH(pTarget, pWindowLogicNode->pProjs) {
7094
0
      if (nodesEqualNode(pFillExpr, pTarget)) {
7095
0
        return targetIdx;
7096
0
      }
7097
0
      ++targetIdx;
7098
0
    }
7099
7100
0
    return -1;
7101
0
  }
7102
7103
0
  int32_t targetIdx = 0;
7104
0
  SNode*  pTarget = NULL;
7105
7106
0
  FOREACH(pTarget, pWindowLogicNode->pFuncs) {
7107
0
    if (nodesEqualNode(pFillExpr, pTarget)) {
7108
0
      return targetIdx;
7109
0
    }
7110
0
    ++targetIdx;
7111
0
  }
7112
7113
0
  FOREACH(pTarget, pWindowLogicNode->pProjs) {
7114
0
    if (nodesEqualNode(pFillExpr, pTarget)) {
7115
0
      return targetIdx;
7116
0
    }
7117
0
    ++targetIdx;
7118
0
  }
7119
7120
0
  return -1;
7121
0
}
7122
7123
static int32_t createExternalWindowPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
7124
0
                                             SWindowLogicNode* pWindowLogicNode, SPhysiNode** pPhyNode) {
7125
0
  int32_t code = TSDB_CODE_SUCCESS;
7126
0
  SExternalWindowPhysiNode* pExternal = (SExternalWindowPhysiNode*)makePhysiNode(
7127
0
      pCxt, (SLogicNode*)pWindowLogicNode, getExternalOperatorType(pWindowLogicNode->windowAlgo));
7128
0
  if (NULL == pExternal) {
7129
0
    return terrno;
7130
0
  }
7131
0
  pExternal->timeRange = pWindowLogicNode->timeRange;
7132
0
  pExternal->isSingleTable = pWindowLogicNode->isSingleTable;
7133
0
  pExternal->inputHasOrder = pWindowLogicNode->inputHasOrder;
7134
0
  pExternal->needGroupSort = pWindowLogicNode->needGroupSort;
7135
0
  pExternal->extWinSplit = pWindowLogicNode->extWinSplit;
7136
0
  pExternal->calcWithPartition = pWindowLogicNode->calcWithPartition;
7137
0
  pExternal->matchPartitionBy = pWindowLogicNode->pSubquery != NULL &&
7138
0
                                nodeType(pWindowLogicNode->pSubquery) == QUERY_NODE_REMOTE_TABLE &&
7139
0
                                ((((SRemoteTableNode*)pWindowLogicNode->pSubquery)->flag &
7140
0
                                  REMOTE_TABLE_FLAG_EXT_PART_KEYS_MATCH) != 0);
7141
0
  pExternal->extWinInputMode = pWindowLogicNode->extWinInputMode;
7142
0
  pExternal->extFill.mode = pWindowLogicNode->extFill.mode;
7143
0
  pExternal->orgTableUid = pWindowLogicNode->orgTableUid;
7144
0
  pExternal->orgTableVgId = pWindowLogicNode->orgTableVgId;
7145
0
  PLAN_ERR_JRET(nodesCloneNode(pWindowLogicNode->pSubquery, &pExternal->pSubquery));
7146
0
  PLAN_ERR_JRET(nodesCloneNode(pWindowLogicNode->pTimeRange, &pExternal->pTimeRange));
7147
0
  PLAN_ERR_JRET(nodesCloneNode(pWindowLogicNode->pWindowConditions, &pExternal->pWindowConditions));
7148
7149
0
  PLAN_ERR_JRET(createWindowPhysiNodeFinalize(pCxt, pChildren, &pExternal->window, pWindowLogicNode));
7150
0
  if (pWindowLogicNode->extFill.pFillExprs != NULL) {
7151
    // Whether targets.len == projs.len (fast path in getExternalWindowOutputIndexByExpr).
7152
    // In this case targetIdx is a pProjs-relative index and we must read the slotId from
7153
    // pExternal->window.pProjs[targetIdx] (TARGET node) because addDataBlockSlots may have
7154
    // created a different slot key for the OPERATOR expression than buildDataBlockSlots
7155
    // created for the corresponding pTargets column.
7156
0
    bool useProjIdx = (LIST_LENGTH(pWindowLogicNode->node.pTargets) == LIST_LENGTH(pWindowLogicNode->pProjs));
7157
7158
0
    SNode* pFillExpr = NULL;
7159
0
    FOREACH(pFillExpr, pWindowLogicNode->extFill.pFillExprs) {
7160
0
      int32_t targetIdx = getExternalWindowOutputIndexByExpr(pWindowLogicNode, pFillExpr);
7161
0
      bool    fromProj = useProjIdx && (targetIdx >= 0);
7162
7163
0
      if (targetIdx < 0) {
7164
0
        const char* pOutputName = getExternalWindowExprOutputName(pFillExpr);
7165
0
        if (pOutputName == NULL) {
7166
0
          PLAN_ERR_JRET(TSDB_CODE_PLAN_INTERNAL_ERROR);
7167
0
        }
7168
7169
0
        SNode* pTarget = NULL;
7170
0
        bool   found = false;
7171
0
        targetIdx = 0;
7172
0
        FOREACH(pTarget, pWindowLogicNode->node.pTargets) {
7173
0
          if (QUERY_NODE_COLUMN == nodeType(pTarget) &&
7174
0
              (0 == strcmp(((SColumnNode*)pTarget)->colName, pOutputName) ||
7175
0
               0 == strcmp(((SExprNode*)pTarget)->aliasName, pOutputName))) {
7176
0
            found = true;
7177
0
            break;
7178
0
          }
7179
0
          ++targetIdx;
7180
0
        }
7181
7182
0
        if (!found) {
7183
0
          PLAN_ERR_JRET(TSDB_CODE_PLAN_INTERNAL_ERROR);
7184
0
        }
7185
0
      }
7186
7187
0
      SNode* pClone = NULL;
7188
0
      if (fromProj) {
7189
        // Use the TARGET node from pProjs to get the correct OPERATOR slot id.
7190
0
        PLAN_ERR_JRET(createExternalWindowOutputColumnByProjIdx(pExternal, targetIdx, &pClone));
7191
0
      } else {
7192
0
        PLAN_ERR_JRET(createExternalWindowOutputColumn(pExternal, targetIdx, &pClone));
7193
0
      }
7194
0
      PLAN_ERR_JRET(nodesListMakeStrictAppend(&pExternal->extFill.pFillExprs, pClone));
7195
0
    }
7196
0
  }
7197
0
  if (pWindowLogicNode->extFill.pFillValues != NULL) {
7198
0
    PLAN_ERR_JRET(nodesCloneNode(pWindowLogicNode->extFill.pFillValues, &pExternal->extFill.pFillValues));
7199
0
  }
7200
0
  *pPhyNode = (SPhysiNode*)pExternal;
7201
7202
0
  return code;
7203
0
_return:
7204
0
  nodesDestroyNode((SNode*)pExternal);
7205
0
  return code;
7206
0
}
7207
7208
static int32_t createWindowPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren, SWindowLogicNode* pWindowLogicNode,
7209
0
                                     SPhysiNode** pPhyNode) {
7210
0
  switch (pWindowLogicNode->winType) {
7211
0
    case WINDOW_TYPE_INTERVAL:
7212
0
      return createIntervalPhysiNode(pCxt, pChildren, pWindowLogicNode, pPhyNode);
7213
0
    case WINDOW_TYPE_SESSION:
7214
0
      return createSessionWindowPhysiNode(pCxt, pChildren, pWindowLogicNode, pPhyNode);
7215
0
    case WINDOW_TYPE_STATE:
7216
0
      return createStateWindowPhysiNode(pCxt, pChildren, pWindowLogicNode, pPhyNode);
7217
0
    case WINDOW_TYPE_EVENT:
7218
0
      return createEventWindowPhysiNode(pCxt, pChildren, pWindowLogicNode, pPhyNode);
7219
0
    case WINDOW_TYPE_COUNT:
7220
0
      return createCountWindowPhysiNode(pCxt, pChildren, pWindowLogicNode, pPhyNode);
7221
0
    case WINDOW_TYPE_ANOMALY:
7222
0
      return createAnomalyWindowPhysiNode(pCxt, pChildren, pWindowLogicNode, pPhyNode);
7223
0
    case WINDOW_TYPE_EXTERNAL:
7224
0
      return createExternalWindowPhysiNode(pCxt, pChildren, pWindowLogicNode, pPhyNode);
7225
0
    default:
7226
0
      return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PLAN_INVALID_WINDOW_TYPE, " invalid window type");
7227
0
  }
7228
0
  return TSDB_CODE_FAILED;
7229
0
}
7230
7231
// createDistinctFilterPhysiNode() was removed with the DistinctAggFilter rule. New logical plans
7232
// no longer contain this node; the physical node and its serialization remain only for already
7233
// materialized plans received from older nodes during rolling upgrades.
7234
7235
static int32_t createSortPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren, SSortLogicNode* pSortLogicNode,
7236
0
                                   SPhysiNode** pPhyNode) {
7237
0
  SSortPhysiNode* pSort = (SSortPhysiNode*)makePhysiNode(
7238
0
      pCxt, (SLogicNode*)pSortLogicNode,
7239
0
      pSortLogicNode->groupSort ? QUERY_NODE_PHYSICAL_PLAN_GROUP_SORT : QUERY_NODE_PHYSICAL_PLAN_SORT);
7240
0
  if (NULL == pSort) {
7241
0
    return terrno;
7242
0
  }
7243
7244
0
  SNodeList* pPrecalcExprs = NULL;
7245
0
  SNodeList* pSortKeys = NULL;
7246
0
  int32_t    code = rewritePrecalcExprs(pCxt, pSortLogicNode->pSortKeys, &pPrecalcExprs, &pSortKeys);
7247
0
  pSort->calcGroupId = pSortLogicNode->calcGroupId;
7248
0
  pSort->excludePkCol = pSortLogicNode->excludePkCol;
7249
0
  pSort->numOfGroupKeys = pSortLogicNode->numOfGroupKeys;
7250
7251
0
  SDataBlockDescNode* pChildTupe = NULL;
7252
0
  if (TSDB_CODE_SUCCESS == code) {
7253
0
    code = getChildTuple(&pChildTupe, pChildren);
7254
0
  }
7255
7256
  // push down expression to pOutputDataBlockDesc of child node
7257
0
  if (TSDB_CODE_SUCCESS == code && NULL != pPrecalcExprs) {
7258
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pPrecalcExprs, &pSort->pExprs);
7259
0
    if (TSDB_CODE_SUCCESS == code) {
7260
0
      code = pushdownDataBlockSlots(pCxt, pSort->pExprs, pChildTupe);
7261
0
    }
7262
0
  }
7263
7264
0
  if (TSDB_CODE_SUCCESS == code) {
7265
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pSortKeys, &pSort->pSortKeys);
7266
0
  }
7267
7268
0
  if (TSDB_CODE_SUCCESS == code) {
7269
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pSortLogicNode->node.pTargets, &pSort->pTargets);
7270
0
    if (TSDB_CODE_SUCCESS == code) {
7271
0
      code = addDataBlockSlots(pCxt, pSort->pTargets, pSort->node.pOutputDataBlockDesc);
7272
0
    }
7273
0
  }
7274
7275
  // For external-window + fill queries: the fill operator writes its output into the
7276
  // ExtWin output block (slots for s1, s2, ...) but Sort only copies columns listed in
7277
  // its pTargets.  Those columns are only in pTargets when they appear in the ORDER BY
7278
  // clause; projection-only columns are missing.  Append them here so Sort passes the
7279
  // fill values through to the parent Project operator.
7280
  //
7281
  // NOTE: This injection only handles the single-node topology where Sort's direct
7282
  // child is ExtWin.  In distributed plans (extWinSplit=true), the plan is split into
7283
  // HASH_EXTERNAL (partition) + Merge + MERGE_ALIGNED_EXTERNAL (coordinator); Sort
7284
  // sits above the Merge node, so this code does not fire.  Fill + ORDER BY in
7285
  // distributed external-window queries is currently not supported.
7286
0
  if (TSDB_CODE_SUCCESS == code) {
7287
0
    SPhysiNode* pChildNode = (SPhysiNode*)nodesListGetNode(pChildren, 0);
7288
0
    if (pChildNode != NULL && (nodeType(pChildNode) == QUERY_NODE_PHYSICAL_PLAN_HASH_EXTERNAL ||
7289
0
                               nodeType(pChildNode) == QUERY_NODE_PHYSICAL_PLAN_MERGE_ALIGNED_EXTERNAL)) {
7290
0
      SExternalWindowPhysiNode* pExtWin = (SExternalWindowPhysiNode*)pChildNode;
7291
0
      if (pExtWin->extFill.pFillExprs != NULL && pExtWin->window.pProjs != NULL) {
7292
        // Iterate ExtWin's pProjs (STargetNode list): each contains the output slotId for
7293
        // a fill-output column (s1, s2, ...).  For each one that is not yet in Sort's
7294
        // pTargets, create a passthrough STargetNode and append it.
7295
0
        SNode* pProjNode = NULL;
7296
0
        FOREACH(pProjNode, pExtWin->window.pProjs) {
7297
0
          if (nodeType(pProjNode) != QUERY_NODE_TARGET) continue;
7298
0
          STargetNode* pExtTarget = (STargetNode*)pProjNode;
7299
0
          int16_t      projSlotId = pExtTarget->slotId;
7300
7301
          // Check if this slot is already covered by Sort's pTargets.
7302
0
          bool alreadyCovered = false;
7303
0
          SNode* pSortTarget = NULL;
7304
0
          FOREACH(pSortTarget, pSort->pTargets) {
7305
0
            if (nodeType(pSortTarget) == QUERY_NODE_TARGET) {
7306
0
              STargetNode* pST = (STargetNode*)pSortTarget;
7307
0
              if (nodeType(pST->pExpr) == QUERY_NODE_COLUMN) {
7308
0
                SColumnNode* pSC = (SColumnNode*)pST->pExpr;
7309
0
                if (pSC->dataBlockId == pChildTupe->dataBlockId && pSC->slotId == projSlotId) {
7310
0
                  alreadyCovered = true;
7311
0
                  break;
7312
0
                }
7313
0
              }
7314
0
            }
7315
0
          }
7316
0
          if (alreadyCovered) continue;
7317
7318
          // Find the corresponding SlotDescNode in ExtWin's output to get the data type.
7319
0
          SSlotDescNode* pSlotDesc = NULL;
7320
0
          SNode* pSlotNode = NULL;
7321
0
          FOREACH(pSlotNode, pExtWin->window.node.pOutputDataBlockDesc->pSlots) {
7322
0
            SSlotDescNode* pSD = (SSlotDescNode*)pSlotNode;
7323
0
            if (pSD->slotId == projSlotId) { pSlotDesc = pSD; break; }
7324
0
          }
7325
0
          if (pSlotDesc == NULL) continue;
7326
7327
          // Create a column node referring to ExtWin's projSlotId.
7328
0
          SColumnNode* pCol = NULL;
7329
0
          code = nodesMakeNode(QUERY_NODE_COLUMN, (SNode**)&pCol);
7330
0
          if (code != TSDB_CODE_SUCCESS) break;
7331
0
          pCol->dataBlockId = pChildTupe->dataBlockId;
7332
0
          pCol->slotId = projSlotId;
7333
0
          pCol->colType = COLUMN_TYPE_COLUMN;
7334
0
          pCol->node.resType = pSlotDesc->dataType;
7335
0
          tstrncpy(pCol->colName, pSlotDesc->name, TSDB_COL_NAME_LEN);
7336
0
          tstrncpy(pCol->node.aliasName, pSlotDesc->name, TSDB_COL_NAME_LEN);
7337
0
          tstrncpy(pCol->node.userAlias, pSlotDesc->name, TSDB_COL_NAME_LEN);
7338
7339
          // Allocate a new slot in Sort's output block.
7340
0
          int16_t nextSlot = (int16_t)LIST_LENGTH(pSort->node.pOutputDataBlockDesc->pSlots);
7341
0
          code = nodesListStrictAppend(pSort->node.pOutputDataBlockDesc->pSlots,
7342
0
                                       createSlotDesc(pCxt, pSlotDesc->name, (SNode*)pCol, nextSlot, true, false));
7343
0
          if (code != TSDB_CODE_SUCCESS) { nodesDestroyNode((SNode*)pCol); break; }
7344
0
          SHashObj* pHash = taosArrayGetP(pCxt->pLocationHelper, pSort->node.pOutputDataBlockDesc->dataBlockId);
7345
0
          int32_t   nameLen = strlen(pSlotDesc->name);
7346
0
          code = putSlotToHashImpl(pSort->node.pOutputDataBlockDesc->dataBlockId, nextSlot,
7347
0
                                   pSlotDesc->name, nameLen, pHash);
7348
0
          if (code != TSDB_CODE_SUCCESS) { nodesDestroyNode((SNode*)pCol); break; }
7349
0
          pSort->node.pOutputDataBlockDesc->totalRowSize += pSlotDesc->dataType.bytes;
7350
0
          pSort->node.pOutputDataBlockDesc->outputRowSize += pSlotDesc->dataType.bytes;
7351
7352
          // Create and append the STargetNode.
7353
0
          SNode* pTarget = NULL;
7354
0
          code = createTarget((SNode*)pCol, pSort->node.pOutputDataBlockDesc->dataBlockId, nextSlot, &pTarget);
7355
0
          if (code != TSDB_CODE_SUCCESS) { nodesDestroyNode((SNode*)pCol); break; }
7356
0
          code = nodesListMakeStrictAppend(&pSort->pTargets, pTarget);
7357
0
          if (code != TSDB_CODE_SUCCESS) { nodesDestroyNode(pTarget); break; }
7358
0
        }
7359
0
      }
7360
0
    }
7361
0
  }
7362
7363
0
  if (TSDB_CODE_SUCCESS == code) {
7364
0
    code = setConditionsSlotId(pCxt, (const SLogicNode*)pSortLogicNode, (SPhysiNode*)pSort);
7365
0
  }
7366
7367
0
  if (TSDB_CODE_SUCCESS == code) {
7368
0
    *pPhyNode = (SPhysiNode*)pSort;
7369
0
  } else {
7370
0
    nodesDestroyNode((SNode*)pSort);
7371
0
  }
7372
7373
0
  nodesDestroyList(pPrecalcExprs);
7374
0
  nodesDestroyList(pSortKeys);
7375
7376
0
  return code;
7377
0
}
7378
7379
static int32_t createPartitionPhysiNodeImpl(SPhysiPlanContext* pCxt, SNodeList* pChildren,
7380
                                            SPartitionLogicNode* pPartLogicNode, ENodeType type,
7381
0
                                            SPhysiNode** pPhyNode) {
7382
0
  SPartitionPhysiNode* pPart = (SPartitionPhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pPartLogicNode, type);
7383
0
  if (NULL == pPart) {
7384
0
    return terrno;
7385
0
  }
7386
7387
0
  SNodeList* pPrecalcExprs = NULL;
7388
0
  SNodeList* pPartitionKeys = NULL;
7389
0
  int32_t    code = rewritePrecalcExprs(pCxt, pPartLogicNode->pPartitionKeys, &pPrecalcExprs, &pPartitionKeys);
7390
0
  pPart->phasedMode = pPartLogicNode->phasedMode;
7391
0
  pPart->needBlockOutputTsOrder = pPartLogicNode->needBlockOutputTsOrder;
7392
0
  SDataBlockDescNode* pChildTupe = NULL;
7393
0
  if (TSDB_CODE_SUCCESS == code) {
7394
0
    code = getChildTuple(&pChildTupe, pChildren);
7395
0
  }
7396
0
  if (TSDB_CODE_SUCCESS == code) {
7397
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pPartLogicNode->node.pTargets, &pPart->pTargets);
7398
0
    if (TSDB_CODE_SUCCESS == code) {
7399
0
      code = addDataBlockSlots(pCxt, pPart->pTargets, pPart->node.pOutputDataBlockDesc);
7400
0
    }
7401
0
  }
7402
  // push down expression to pOutputDataBlockDesc of child node
7403
0
  if (TSDB_CODE_SUCCESS == code && NULL != pPrecalcExprs) {
7404
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pPrecalcExprs, &pPart->pExprs);
7405
0
    if (TSDB_CODE_SUCCESS == code) {
7406
0
      code = pushdownDataBlockSlots(pCxt, pPart->pExprs, pChildTupe);
7407
0
    }
7408
0
  }
7409
7410
  // Convert pseudo-column functions (e.g. TBNAME) in partition keys to column
7411
  // nodes.  doSetSlotId only resolves QUERY_NODE_COLUMN; a FUNCTION node passes
7412
  // through with an uninitialised slotId, and the executor's
7413
  // makeColumnArrayFromList then casts it to SColumnNode*  =>  garbage slotId/type
7414
  //  =>  buffer overflow in doHashPartition.  Replacing with a COLUMN whose
7415
  // colName matches the slot added by addDataBlockSlots(pScanPseudoCols) lets
7416
  // doSetSlotId resolve the correct slotId.
7417
0
  if (TSDB_CODE_SUCCESS == code) {
7418
0
    SNode* pKeyNode = NULL;
7419
0
    FOREACH(pKeyNode, pPartitionKeys) {
7420
0
      if (QUERY_NODE_FUNCTION == nodeType(pKeyNode) &&
7421
0
          fmIsScanPseudoColumnFunc(((SFunctionNode*)pKeyNode)->funcId)) {
7422
0
        SFunctionNode* pFunc = (SFunctionNode*)pKeyNode;
7423
0
        SColumnNode*   pCol  = NULL;
7424
0
        code = nodesMakeNode(QUERY_NODE_COLUMN, (SNode**)&pCol);
7425
0
        if (TSDB_CODE_SUCCESS != code) break;
7426
0
        pCol->node.resType = pFunc->node.resType;
7427
0
        tstrncpy(pCol->colName, pFunc->node.aliasName, TSDB_COL_NAME_LEN);
7428
0
        tstrncpy(pCol->node.aliasName, pFunc->node.aliasName, sizeof(pCol->node.aliasName));
7429
0
        tstrncpy(pCol->node.userAlias, pFunc->node.userAlias, sizeof(pCol->node.userAlias));
7430
0
        nodesDestroyNode(pKeyNode);
7431
0
        REPLACE_NODE(pCol);
7432
0
      }
7433
0
    }
7434
0
  }
7435
7436
0
  if (TSDB_CODE_SUCCESS == code) {
7437
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pPartitionKeys, &pPart->pPartitionKeys);
7438
0
  }
7439
7440
0
  if (pPart->needBlockOutputTsOrder) {
7441
0
    SNode* node;
7442
0
    bool   found = false;
7443
    // Iterate the PHYSICAL targets (pPart->pTargets) to get correct physical slot IDs.
7444
    // pPartLogicNode->node.pTargets has logical slotIds (often 0) which are wrong here.
7445
0
    FOREACH(node, pPart->pTargets) {
7446
0
      if (nodeType(node) == QUERY_NODE_TARGET) {
7447
0
        STargetNode* pTgt = (STargetNode*)node;
7448
0
        if (nodeType(pTgt->pExpr) == QUERY_NODE_COLUMN) {
7449
0
          SColumnNode* pCol = (SColumnNode*)pTgt->pExpr;
7450
0
          if (pCol->tableId == pPartLogicNode->pkTsColTbId && pCol->colId == pPartLogicNode->pkTsColId) {
7451
0
            pPart->tsSlotId = pTgt->slotId;
7452
0
            found = true;
7453
0
            break;
7454
0
          }
7455
0
        }
7456
0
      }
7457
0
    }
7458
0
    if (!found) code = TSDB_CODE_PLAN_INTERNAL_ERROR;
7459
0
  }
7460
7461
0
  if (TSDB_CODE_SUCCESS == code) {
7462
0
    code = setConditionsSlotId(pCxt, (const SLogicNode*)pPartLogicNode, (SPhysiNode*)pPart);
7463
0
  }
7464
7465
0
  if (TSDB_CODE_SUCCESS == code) {
7466
0
    *pPhyNode = (SPhysiNode*)pPart;
7467
0
  } else {
7468
0
    nodesDestroyNode((SNode*)pPart);
7469
0
  }
7470
7471
0
  nodesDestroyList(pPrecalcExprs);
7472
0
  nodesDestroyList(pPartitionKeys);
7473
7474
0
  return code;
7475
0
}
7476
7477
static int32_t createPartitionPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren,
7478
0
                                        SPartitionLogicNode* pPartLogicNode, SPhysiNode** pPhyNode) {
7479
0
  return createPartitionPhysiNodeImpl(pCxt, pChildren, pPartLogicNode, QUERY_NODE_PHYSICAL_PLAN_PARTITION, pPhyNode);
7480
0
}
7481
7482
static int32_t createFillPhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren, SFillLogicNode* pFillNode,
7483
0
                                   SPhysiNode** pPhyNode) {
7484
0
  SFillPhysiNode* pFill = (SFillPhysiNode*)makePhysiNode(
7485
0
      pCxt, (SLogicNode*)pFillNode, QUERY_NODE_PHYSICAL_PLAN_FILL);
7486
0
  if (NULL == pFill) {
7487
0
    return terrno;
7488
0
  }
7489
7490
0
  pFill->mode = pFillNode->mode;
7491
0
  pFill->indefRowsMode = pFillNode->indefRowsMode;
7492
0
  pFill->interval = pFillNode->interval;
7493
0
  pFill->offset = pFillNode->offset;
7494
0
  pFill->sliding = pFillNode->sliding;
7495
0
  pFill->intervalUnit = pFillNode->intervalUnit;
7496
0
  pFill->slidingUnit = pFillNode->slidingUnit;
7497
0
  pFill->firstDayOfWeek = pFillNode->firstDayOfWeek;
7498
0
  pFill->timezone = pFillNode->timezone;
7499
0
  pFill->ownsTimezone = false;
7500
0
  tstrncpy(pFill->timezoneName, pFillNode->timezoneName, sizeof(pFill->timezoneName));
7501
0
  pFill->timeRange = pFillNode->timeRange;
7502
0
  TSWAP(pFill->pTimeRange, pFillNode->pTimeRange);
7503
0
  pFill->node.inputTsOrder = pFillNode->node.inputTsOrder;
7504
0
  int32_t             code = TSDB_CODE_SUCCESS;
7505
0
  SDataBlockDescNode* pChildTupe = NULL;
7506
0
  if (TSDB_CODE_SUCCESS == code) {
7507
0
    code = getChildTuple(&pChildTupe, pChildren);
7508
0
  }
7509
0
  if (TSDB_CODE_SUCCESS == code) {
7510
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pFillNode->pFillExprs, &pFill->pFillExprs);
7511
0
  }
7512
0
  if (TSDB_CODE_SUCCESS == code) {
7513
0
    code = addDataBlockSlots(pCxt, pFill->pFillExprs, pFill->node.pOutputDataBlockDesc);
7514
0
  }
7515
0
  if (TSDB_CODE_SUCCESS == code) {
7516
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pFillNode->pNotFillExprs, &pFill->pNotFillExprs);
7517
0
  }
7518
0
  if (TSDB_CODE_SUCCESS == code) {
7519
0
    code = addDataBlockSlots(pCxt, pFill->pNotFillExprs, pFill->node.pOutputDataBlockDesc);
7520
0
  }
7521
0
  if (TSDB_CODE_SUCCESS == code && LIST_LENGTH(pFillNode->pFillNullExprs) > 0) {
7522
0
    code = setListSlotId(pCxt, pChildTupe->dataBlockId, -1, pFillNode->pFillNullExprs, &pFill->pFillNullExprs);
7523
0
    if (TSDB_CODE_SUCCESS == code) {
7524
0
      code = addDataBlockSlots(pCxt, pFill->pFillNullExprs, pFill->node.pOutputDataBlockDesc);
7525
0
    }
7526
0
  }
7527
7528
0
  if (TSDB_CODE_SUCCESS == code) {
7529
0
    code = setNodeSlotId(pCxt, pChildTupe->dataBlockId, -1, pFillNode->pWStartTs, &pFill->pWStartTs);
7530
0
  }
7531
0
  if (TSDB_CODE_SUCCESS == code) {
7532
0
    code = addDataBlockSlot(pCxt, &pFill->pWStartTs, pFill->node.pOutputDataBlockDesc);
7533
0
  }
7534
7535
0
  if (TSDB_CODE_SUCCESS == code && NULL != pFillNode->pValues) {
7536
0
    code = nodesCloneNode(pFillNode->pValues, &pFill->pValues);
7537
0
  }
7538
7539
0
  if (TSDB_CODE_SUCCESS == code && NULL != pFillNode->pSurroundingTime) {
7540
0
    code = nodesCloneNode(pFillNode->pSurroundingTime,
7541
0
                          &pFill->pSurroundingTime);
7542
0
  }
7543
7544
0
  if (TSDB_CODE_SUCCESS == code) {
7545
0
    code = setConditionsSlotId(pCxt, (const SLogicNode*)pFillNode, (SPhysiNode*)pFill);
7546
0
  }
7547
7548
0
  if (TSDB_CODE_SUCCESS == code) {
7549
0
    *pPhyNode = (SPhysiNode*)pFill;
7550
0
  } else {
7551
0
    nodesDestroyNode((SNode*)pFill);
7552
0
  }
7553
7554
0
  return code;
7555
0
}
7556
7557
0
static int32_t createExchangePhysiNodeByMerge(SMergePhysiNode* pMerge, int32_t idx, bool grpSingleChannel) {
7558
0
  SExchangePhysiNode* pExchange = NULL;
7559
0
  int32_t             code = nodesMakeNode(QUERY_NODE_PHYSICAL_PLAN_EXCHANGE, (SNode**)&pExchange);
7560
0
  if (NULL == pExchange) {
7561
0
    return code;
7562
0
  }
7563
0
  pExchange->srcStartGroupId = pMerge->srcGroupId + idx;
7564
0
  pExchange->srcEndGroupId = pMerge->srcGroupId + idx;
7565
0
  pExchange->grpSingleChannel = grpSingleChannel;
7566
0
  pExchange->singleSrc = true;
7567
0
  pExchange->node.dynamicOp = pMerge->node.dynamicOp;
7568
0
  pExchange->node.pParent = (SPhysiNode*)pMerge;
7569
0
  pExchange->node.pOutputDataBlockDesc = NULL;
7570
0
  code = nodesCloneNode((SNode*)pMerge->node.pOutputDataBlockDesc, (SNode**)&pExchange->node.pOutputDataBlockDesc);
7571
0
  if (NULL == pExchange->node.pOutputDataBlockDesc) {
7572
0
    nodesDestroyNode((SNode*)pExchange);
7573
0
    return code;
7574
0
  }
7575
0
  SNode* pSlot = NULL;
7576
0
  FOREACH(pSlot, pExchange->node.pOutputDataBlockDesc->pSlots) { ((SSlotDescNode*)pSlot)->output = true; }
7577
0
  return nodesListMakeStrictAppend(&pMerge->node.pChildren, (SNode*)pExchange);
7578
0
}
7579
7580
static int32_t createMergePhysiNode(SPhysiPlanContext* pCxt, SNodeList* pChildren, SMergeLogicNode* pMergeLogicNode,
7581
0
                                    SPhysiNode** pPhyNode) {
7582
0
  int32_t          code = TSDB_CODE_SUCCESS;
7583
0
  SMergePhysiNode* pMerge =
7584
0
      (SMergePhysiNode*)makePhysiNode(pCxt, (SLogicNode*)pMergeLogicNode, QUERY_NODE_PHYSICAL_PLAN_MERGE);
7585
0
  if (NULL == pMerge) {
7586
0
    return terrno;
7587
0
  }
7588
7589
0
  if (pMergeLogicNode->colsMerge) {
7590
0
    pMerge->type = MERGE_TYPE_COLUMNS;
7591
0
  } else if (pMergeLogicNode->needSort) {
7592
0
    pMerge->type = MERGE_TYPE_SORT;
7593
0
  } else {
7594
0
    pMerge->type = MERGE_TYPE_NON_SORT;
7595
0
  }
7596
7597
0
  pMerge->numOfChannels = pMergeLogicNode->numOfChannels;
7598
0
  pMerge->srcGroupId = pMergeLogicNode->srcGroupId;
7599
0
  pMerge->srcEndGroupId = pMergeLogicNode->srcEndGroupId;
7600
0
  pMerge->groupSort = pMergeLogicNode->groupSort;
7601
0
  pMerge->ignoreGroupId = pMergeLogicNode->ignoreGroupId;
7602
0
  pMerge->inputWithGroupId = pMergeLogicNode->inputWithGroupId;
7603
0
  pMerge->outerGroupPhaseMode = pMergeLogicNode->outerGroupPhaseMode;
7604
7605
0
  if (!pMergeLogicNode->colsMerge) {
7606
0
    PLAN_ERR_JRET(addDataBlockSlots(pCxt, pMergeLogicNode->pInputs, pMerge->node.pOutputDataBlockDesc));
7607
7608
0
    for (int32_t j = 0; j < pMergeLogicNode->numOfSubplans; ++j) {
7609
0
      for (int32_t i = 0; i < pMerge->numOfChannels; ++i) {
7610
0
        PLAN_ERR_JRET(createExchangePhysiNodeByMerge(pMerge, j, pMerge->numOfChannels == 1));
7611
0
      }
7612
0
    }
7613
7614
0
    if (NULL != pMergeLogicNode->pMergeKeys) {
7615
0
      PLAN_ERR_JRET(setListSlotId(pCxt, pMerge->node.pOutputDataBlockDesc->dataBlockId, -1, pMergeLogicNode->pMergeKeys,
7616
0
                                  &pMerge->pMergeKeys));
7617
0
    }
7618
7619
0
    PLAN_ERR_JRET(setListSlotId(pCxt, pMerge->node.pOutputDataBlockDesc->dataBlockId, -1, pMergeLogicNode->node.pTargets,
7620
0
                                &pMerge->pTargets));
7621
0
    PLAN_ERR_JRET(addDataBlockSlots(pCxt, pMerge->pTargets, pMerge->node.pOutputDataBlockDesc));
7622
0
  } else {
7623
0
    PLAN_ERR_JRET(setMultiBlockSlotId(pCxt, pChildren, false, pMergeLogicNode->node.pTargets, &pMerge->pTargets));
7624
0
    PLAN_ERR_JRET(addDataBlockSlots(pCxt, pMerge->pTargets, pMerge->node.pOutputDataBlockDesc));
7625
0
  }
7626
7627
0
  *pPhyNode = (SPhysiNode*)pMerge;
7628
0
  return code;
7629
0
_return:
7630
0
  nodesDestroyNode((SNode*)pMerge);
7631
0
  return code;
7632
0
}
7633
7634
static int32_t doCreatePhysiNode(SPhysiPlanContext* pCxt, SLogicNode* pLogicNode, SSubplan* pSubplan,
7635
0
                                 SNodeList* pChildren, SPhysiNode** pPhyNode) {
7636
0
  switch (nodeType(pLogicNode)) {
7637
0
    case QUERY_NODE_LOGIC_PLAN_SCAN:
7638
0
      return createScanPhysiNode(pCxt, pSubplan, (SScanLogicNode*)pLogicNode, pPhyNode);
7639
0
    case QUERY_NODE_LOGIC_PLAN_JOIN:
7640
0
      return createJoinPhysiNode(pCxt, pChildren, (SJoinLogicNode*)pLogicNode, pPhyNode);
7641
0
    case QUERY_NODE_LOGIC_PLAN_AGG:
7642
0
      return createAggPhysiNode(pCxt, pChildren, (SAggLogicNode*)pLogicNode, pPhyNode, pSubplan);
7643
0
    case QUERY_NODE_LOGIC_PLAN_PROJECT:
7644
0
      return createProjectPhysiNode(pCxt, pChildren, (SProjectLogicNode*)pLogicNode, pSubplan, pPhyNode);
7645
0
    case QUERY_NODE_LOGIC_PLAN_EXCHANGE:
7646
0
      return createExchangePhysiNode(pCxt, (SExchangeLogicNode*)pLogicNode, pPhyNode);
7647
0
    case QUERY_NODE_LOGIC_PLAN_WINDOW:
7648
0
      return createWindowPhysiNode(pCxt, pChildren, (SWindowLogicNode*)pLogicNode, pPhyNode);
7649
0
    case QUERY_NODE_LOGIC_PLAN_SORT:
7650
0
      return createSortPhysiNode(pCxt, pChildren, (SSortLogicNode*)pLogicNode, pPhyNode);
7651
0
    case QUERY_NODE_LOGIC_PLAN_WINDOW_FUNC:
7652
0
      return createWindowFuncPhysiNode(pCxt, pChildren, (SWindowFuncLogicNode*)pLogicNode, pPhyNode);
7653
0
    case QUERY_NODE_LOGIC_PLAN_PARTITION:
7654
0
      return createPartitionPhysiNode(pCxt, pChildren, (SPartitionLogicNode*)pLogicNode, pPhyNode);
7655
0
    case QUERY_NODE_LOGIC_PLAN_FILL:
7656
0
      return createFillPhysiNode(pCxt, pChildren, (SFillLogicNode*)pLogicNode, pPhyNode);
7657
0
    case QUERY_NODE_LOGIC_PLAN_INDEF_ROWS_FUNC:
7658
0
      return createIndefRowsFuncPhysiNode(pCxt, pChildren, (SIndefRowsFuncLogicNode*)pLogicNode, pPhyNode);
7659
0
    case QUERY_NODE_LOGIC_PLAN_INTERP_FUNC:
7660
0
      return createInterpFuncPhysiNode(pCxt, pChildren, (SInterpFuncLogicNode*)pLogicNode, pPhyNode);
7661
0
    case QUERY_NODE_LOGIC_PLAN_ANALYSIS_FUNC:
7662
0
      return createGenericAnalysisPhysiNode(pCxt, pChildren, (SGenericAnalysisLogicNode*)pLogicNode, pPhyNode);
7663
0
    case QUERY_NODE_LOGIC_PLAN_FORECAST_FUNC:
7664
0
      return createForecastFuncPhysiNode(pCxt, pChildren, (SForecastFuncLogicNode*)pLogicNode, pPhyNode);
7665
0
    case QUERY_NODE_LOGIC_PLAN_ROWSET_SOURCE:
7666
0
      return createRowsetSourcePhysiNode(pCxt, (SRowsetSourceLogicNode*)pLogicNode, pPhyNode);
7667
0
    case QUERY_NODE_LOGIC_PLAN_MERGE:
7668
0
      return createMergePhysiNode(pCxt, pChildren, (SMergeLogicNode*)pLogicNode, pPhyNode);
7669
0
    case QUERY_NODE_LOGIC_PLAN_GROUP_CACHE:
7670
0
      return createGroupCachePhysiNode(pCxt, pChildren, (SGroupCacheLogicNode*)pLogicNode, pPhyNode);
7671
0
    case QUERY_NODE_LOGIC_PLAN_DYN_QUERY_CTRL:
7672
0
      return createDynQueryCtrlPhysiNode(pCxt, pChildren, (SDynQueryCtrlLogicNode*)pLogicNode, pPhyNode, pSubplan);
7673
0
    case QUERY_NODE_LOGIC_PLAN_VIRTUAL_TABLE_SCAN:
7674
0
      return createVirtualTableScanPhysiNode(pCxt, pSubplan, pChildren, (SVirtualScanLogicNode*)pLogicNode, pPhyNode);
7675
0
    case QUERY_NODE_LOGIC_PLAN_TAG_REF_SOURCE:
7676
0
      return createTagRefSourcePhysiNode(pCxt, pSubplan, pChildren, (STagRefSourceLogicNode*)pLogicNode, pPhyNode);
7677
0
    default:
7678
0
      break;
7679
0
  }
7680
7681
0
  return TSDB_CODE_FAILED;
7682
0
}
7683
7684
static int32_t createPhysiNode(SPhysiPlanContext* pCxt, SLogicNode* pLogicNode, SSubplan* pSubplan,
7685
0
                               SPhysiNode** pPhyNode) {
7686
0
  SNodeList* pChildren = NULL;
7687
0
  int32_t    code = nodesMakeList(&pChildren);
7688
0
  if (NULL == pChildren) {
7689
0
    return code;
7690
0
  }
7691
7692
0
  SNode* pLogicChild;
7693
0
  FOREACH(pLogicChild, pLogicNode->pChildren) {
7694
0
    SPhysiNode* pChild = NULL;
7695
0
    code = createPhysiNode(pCxt, (SLogicNode*)pLogicChild, pSubplan, &pChild);
7696
0
    if (TSDB_CODE_SUCCESS == code) {
7697
0
      code = nodesListStrictAppend(pChildren, (SNode*)pChild);
7698
0
    }
7699
0
    if (TSDB_CODE_SUCCESS != code) {
7700
0
      break;
7701
0
    }
7702
0
  }
7703
7704
0
  if (TSDB_CODE_SUCCESS == code) {
7705
0
    code = doCreatePhysiNode(pCxt, pLogicNode, pSubplan, pChildren, pPhyNode);
7706
0
  }
7707
7708
0
  if (TSDB_CODE_SUCCESS == code) {
7709
0
    if (LIST_LENGTH(pChildren) > 0) {
7710
0
      (*pPhyNode)->pChildren = pChildren;
7711
0
      SNode* pChild;
7712
0
      FOREACH(pChild, (*pPhyNode)->pChildren) { ((SPhysiNode*)pChild)->pParent = (*pPhyNode); }
7713
0
    } else {
7714
0
      nodesDestroyList(pChildren);
7715
0
    }
7716
0
  } else {
7717
0
    nodesDestroyList(pChildren);
7718
0
  }
7719
7720
0
  return code;
7721
0
}
7722
7723
0
static int32_t createDataInserter(SPhysiPlanContext* pCxt, SVgDataBlocks* pBlocks, SDataSinkNode** pSink) {
7724
0
  SDataInserterNode* pInserter = NULL;
7725
0
  int32_t            code = nodesMakeNode(QUERY_NODE_PHYSICAL_PLAN_INSERT, (SNode**)&pInserter);
7726
0
  if (NULL == pInserter) {
7727
0
    return code;
7728
0
  }
7729
7730
0
  pInserter->numOfTables = pBlocks->numOfTables;
7731
0
  pInserter->size = pBlocks->size;
7732
0
  pInserter->pData = pBlocks->pData;
7733
0
  pBlocks->pData = NULL;
7734
7735
0
  *pSink = (SDataSinkNode*)pInserter;
7736
0
  return TSDB_CODE_SUCCESS;
7737
0
}
7738
7739
static int32_t createDataDispatcher(SPhysiPlanContext* pCxt, const SPhysiNode* pRoot, bool dynamicSchema,
7740
0
                                    bool localBlockExchange, SDataSinkNode** pSink) {
7741
0
  SDataDispatcherNode* pDispatcher = NULL;
7742
0
  int32_t              code = nodesMakeNode(QUERY_NODE_PHYSICAL_PLAN_DISPATCH, (SNode**)&pDispatcher);
7743
0
  if (NULL == pDispatcher) {
7744
0
    return code;
7745
0
  }
7746
7747
0
  pDispatcher->sink.pInputDataBlockDesc = NULL;
7748
0
  code = nodesCloneNode((SNode*)pRoot->pOutputDataBlockDesc, (SNode**)&pDispatcher->sink.pInputDataBlockDesc);
7749
0
  if (NULL == pDispatcher->sink.pInputDataBlockDesc) {
7750
0
    nodesDestroyNode((SNode*)pDispatcher);
7751
0
    return code;
7752
0
  }
7753
0
  pDispatcher->dynamicSchema = dynamicSchema;
7754
0
  pDispatcher->localBlockExchange = localBlockExchange;
7755
7756
0
  *pSink = (SDataSinkNode*)pDispatcher;
7757
0
  return TSDB_CODE_SUCCESS;
7758
0
}
7759
7760
0
static int32_t makeSubplan(SPhysiPlanContext* pCxt, SLogicSubplan* pLogicSubplan, SSubplan** ppSubplan) {
7761
0
  SSubplan* pSubplan = NULL;
7762
0
  int32_t   code = nodesMakeNode(QUERY_NODE_PHYSICAL_SUBPLAN, (SNode**)&pSubplan);
7763
0
  if (NULL == pSubplan) {
7764
0
    return code;
7765
0
  }
7766
0
  pSubplan->id = pLogicSubplan->id;
7767
0
  pSubplan->subplanType = pLogicSubplan->subplanType;
7768
0
  pSubplan->level = pLogicSubplan->level;
7769
0
  pSubplan->rowsThreshold = 4096;
7770
0
  pSubplan->dynamicRowThreshold = false;
7771
0
  pSubplan->isView = pCxt->pPlanCxt->isView;
7772
0
  pSubplan->isAudit = pCxt->pPlanCxt->isAudit;
7773
0
  pSubplan->userAppId = pCxt->pPlanCxt->userAppId;
7774
0
  pSubplan->processOneBlock = pLogicSubplan->processOneBlock;
7775
0
  pSubplan->dynTbname = pLogicSubplan->dynTbname;
7776
0
  pSubplan->localBlockExchange = (pLogicSubplan->splitFlag & SUBPLAN_SPLIT_FLAG_LOCAL_BLOCK_EXCHANGE) != 0;
7777
0
  pSubplan->execOnChildNode = pLogicSubplan->execOnChildNode;
7778
0
  if (NULL != pCxt->pPlanCxt->pUser) {
7779
0
    snprintf(pSubplan->user, sizeof(pSubplan->user), "%s", pCxt->pPlanCxt->pUser);
7780
0
  }
7781
0
  *ppSubplan = pSubplan;
7782
0
  return code;
7783
0
}
7784
7785
0
static int32_t buildInsertValuesSubplan(SPhysiPlanContext* pCxt, SVnodeModifyLogicNode* pModify, SSubplan* pSubplan) {
7786
0
  pSubplan->msgType = pModify->msgType;
7787
0
  pSubplan->execNode.nodeId = pModify->pVgDataBlocks->vg.vgId;
7788
0
  pSubplan->execNode.epSet = pModify->pVgDataBlocks->vg.epSet;
7789
0
  return createDataInserter(pCxt, pModify->pVgDataBlocks, &pSubplan->pDataSink);
7790
0
}
7791
7792
static int32_t createQueryInserter(SPhysiPlanContext* pCxt, SVnodeModifyLogicNode* pModify, SSubplan* pSubplan,
7793
0
                                   SDataSinkNode** pSink) {
7794
0
  SQueryInserterNode* pInserter = NULL;
7795
0
  int32_t             code = nodesMakeNode(QUERY_NODE_PHYSICAL_PLAN_QUERY_INSERT, (SNode**)&pInserter);
7796
0
  if (NULL == pInserter) {
7797
0
    return code;
7798
0
  }
7799
7800
0
  pInserter->tableId = pModify->tableId;
7801
0
  pInserter->stableId = pModify->stableId;
7802
0
  pInserter->tableType = pModify->tableType;
7803
0
  tstrncpy(pInserter->tableName, pModify->tableName, TSDB_TABLE_NAME_LEN);
7804
0
  pInserter->explain = (QUERY_NODE_EXPLAIN_STMT == nodeType(pCxt->pPlanCxt->pAstRoot) ? true : false);
7805
0
  if (pModify->pVgroupList) {
7806
0
    pInserter->vgId = pModify->pVgroupList->vgroups[0].vgId;
7807
0
    pInserter->epSet = pModify->pVgroupList->vgroups[0].epSet;
7808
0
    vgroupInfoToNodeAddr(pModify->pVgroupList->vgroups, &pSubplan->execNode);
7809
0
  }
7810
0
  code = setListSlotId(pCxt, pSubplan->pNode->pOutputDataBlockDesc->dataBlockId, -1, pModify->pInsertCols,
7811
0
                       &pInserter->pCols);
7812
0
  if (TSDB_CODE_SUCCESS == code) {
7813
0
    pInserter->sink.pInputDataBlockDesc = NULL;
7814
0
    code = nodesCloneNode((SNode*)pSubplan->pNode->pOutputDataBlockDesc, (SNode**)&pInserter->sink.pInputDataBlockDesc);
7815
0
  }
7816
7817
0
  if (TSDB_CODE_SUCCESS == code) {
7818
0
    *pSink = (SDataSinkNode*)pInserter;
7819
0
  } else {
7820
0
    nodesDestroyNode((SNode*)pInserter);
7821
0
  }
7822
7823
0
  return code;
7824
0
}
7825
7826
0
static int32_t buildInsertSelectSubplan(SPhysiPlanContext* pCxt, SVnodeModifyLogicNode* pModify, SSubplan* pSubplan) {
7827
0
  int32_t code =
7828
0
      createPhysiNode(pCxt, (SLogicNode*)nodesListGetNode(pModify->node.pChildren, 0), pSubplan, &pSubplan->pNode);
7829
0
  if (TSDB_CODE_SUCCESS == code) {
7830
0
    code = createQueryInserter(pCxt, pModify, pSubplan, &pSubplan->pDataSink);
7831
0
  }
7832
0
  pSubplan->msgType = TDMT_SCH_MERGE_QUERY;
7833
0
  return code;
7834
0
}
7835
7836
0
static int32_t buildInsertSubplan(SPhysiPlanContext* pCxt, SVnodeModifyLogicNode* pModify, SSubplan* pSubplan) {
7837
0
  if (NULL == pModify->node.pChildren) {
7838
0
    return buildInsertValuesSubplan(pCxt, pModify, pSubplan);
7839
0
  }
7840
0
  return buildInsertSelectSubplan(pCxt, pModify, pSubplan);
7841
0
}
7842
7843
static int32_t createDataDeleter(SPhysiPlanContext* pCxt, SVnodeModifyLogicNode* pModify, const SPhysiNode* pRoot,
7844
0
                                 SDataSinkNode** pSink) {
7845
0
  SDataDeleterNode* pDeleter = NULL;
7846
0
  int32_t           code = nodesMakeNode(QUERY_NODE_PHYSICAL_PLAN_DELETE, (SNode**)&pDeleter);
7847
0
  if (NULL == pDeleter) {
7848
0
    return code;
7849
0
  }
7850
7851
0
  pDeleter->tableId = pModify->tableId;
7852
0
  pDeleter->tableType = pModify->tableType;
7853
0
  tstrncpy(pDeleter->tableFName, pModify->tableName, TSDB_TABLE_NAME_LEN);
7854
0
  tstrncpy(pDeleter->tsColName, pModify->tsColName, TSDB_COL_NAME_LEN);
7855
0
  pDeleter->deleteTimeRange = pModify->deleteTimeRange;
7856
0
  pDeleter->secureDelete = pModify->secureDelete;
7857
0
  pDeleter->rangeMode = pModify->deleteRangeMode;
7858
0
  pDeleter->tsSlotId = -1;
7859
7860
0
  if (pDeleter->rangeMode) {
7861
    // pStartTs carries the ts column here, not first(ts); resolve it against the scan output so the
7862
    // deleter can read the timestamps out of every incoming block.
7863
0
    code = setNodeSlotId(pCxt, pRoot->pOutputDataBlockDesc->dataBlockId, -1, pModify->pStartTs, &pDeleter->pStartTs);
7864
0
    if (TSDB_CODE_SUCCESS == code) {
7865
0
      if (QUERY_NODE_COLUMN != nodeType(pDeleter->pStartTs)) {
7866
0
        nodesDestroyNode((SNode*)pDeleter);
7867
0
        return TSDB_CODE_PLAN_INTERNAL_ERROR;
7868
0
      }
7869
0
      pDeleter->tsSlotId = ((SColumnNode*)pDeleter->pStartTs)->slotId;
7870
0
    }
7871
0
  } else {
7872
0
    code = setNodeSlotId(pCxt, pRoot->pOutputDataBlockDesc->dataBlockId, -1, pModify->pAffectedRows,
7873
0
                         &pDeleter->pAffectedRows);
7874
0
    if (TSDB_CODE_SUCCESS == code) {
7875
0
      code = setNodeSlotId(pCxt, pRoot->pOutputDataBlockDesc->dataBlockId, -1, pModify->pStartTs, &pDeleter->pStartTs);
7876
0
    }
7877
0
    if (TSDB_CODE_SUCCESS == code) {
7878
0
      code = setNodeSlotId(pCxt, pRoot->pOutputDataBlockDesc->dataBlockId, -1, pModify->pEndTs, &pDeleter->pEndTs);
7879
0
    }
7880
0
  }
7881
7882
0
  if (TSDB_CODE_SUCCESS == code) {
7883
0
    pDeleter->sink.pInputDataBlockDesc = NULL;
7884
0
    code = nodesCloneNode((SNode*)pRoot->pOutputDataBlockDesc, (SNode**)&pDeleter->sink.pInputDataBlockDesc);
7885
0
  }
7886
7887
0
  if (TSDB_CODE_SUCCESS == code) {
7888
0
    *pSink = (SDataSinkNode*)pDeleter;
7889
0
  } else {
7890
0
    nodesDestroyNode((SNode*)pDeleter);
7891
0
  }
7892
7893
0
  return code;
7894
0
}
7895
7896
0
static int32_t buildDeleteSubplan(SPhysiPlanContext* pCxt, SVnodeModifyLogicNode* pModify, SSubplan* pSubplan) {
7897
0
  int32_t code =
7898
0
      createPhysiNode(pCxt, (SLogicNode*)nodesListGetNode(pModify->node.pChildren, 0), pSubplan, &pSubplan->pNode);
7899
0
  if (TSDB_CODE_SUCCESS == code) {
7900
0
    code = createDataDeleter(pCxt, pModify, pSubplan->pNode, &pSubplan->pDataSink);
7901
0
  }
7902
0
  pSubplan->msgType = TDMT_VND_DELETE;
7903
0
  return code;
7904
0
}
7905
7906
0
static int32_t buildVnodeModifySubplan(SPhysiPlanContext* pCxt, SLogicSubplan* pLogicSubplan, SSubplan* pSubplan) {
7907
0
  int32_t                code = TSDB_CODE_SUCCESS;
7908
0
  SVnodeModifyLogicNode* pModify = (SVnodeModifyLogicNode*)pLogicSubplan->pNode;
7909
0
  switch (pModify->modifyType) {
7910
0
    case MODIFY_TABLE_TYPE_INSERT:
7911
0
      code = buildInsertSubplan(pCxt, pModify, pSubplan);
7912
0
      break;
7913
0
    case MODIFY_TABLE_TYPE_DELETE:
7914
0
      code = buildDeleteSubplan(pCxt, pModify, pSubplan);
7915
0
      break;
7916
0
    default:
7917
0
      code = TSDB_CODE_FAILED;
7918
0
      break;
7919
0
  }
7920
0
  return code;
7921
0
}
7922
7923
0
static int32_t createPhysiSubplan(SPhysiPlanContext* pCxt, SLogicSubplan* pLogicSubplan, SSubplan** pPhysiSubplan) {
7924
0
  SSubplan* pSubplan = NULL;
7925
0
  int32_t   code = makeSubplan(pCxt, pLogicSubplan, &pSubplan);
7926
0
  if (NULL == pSubplan) {
7927
0
    return code;
7928
0
  }
7929
7930
  // Set execNode from logic subplan's vgroup list
7931
  // After split phase, vgroup list is transferred from logic node to logic subplan via TSWAP
7932
  // We need to set execNode here before creating physical nodes
7933
0
  if (pLogicSubplan->pVgroupList && pLogicSubplan->pVgroupList->numOfVgroups > 0) {
7934
0
    vgroupInfoToNodeAddr(pLogicSubplan->pVgroupList->vgroups, &pSubplan->execNode);
7935
0
    pSubplan->execNodeStat.tableNum = pLogicSubplan->pVgroupList->vgroups[0].numOfTable;
7936
0
  }
7937
7938
0
  if (SUBPLAN_TYPE_MODIFY == pLogicSubplan->subplanType) {
7939
0
    code = buildVnodeModifySubplan(pCxt, pLogicSubplan, pSubplan);
7940
0
  } else {
7941
0
    if (SUBPLAN_TYPE_SCAN == pSubplan->subplanType || SUBPLAN_TYPE_HSYSSCAN == pSubplan->subplanType) {
7942
0
      pSubplan->msgType = TDMT_SCH_QUERY;
7943
0
    } else {
7944
0
      pSubplan->msgType = TDMT_SCH_MERGE_QUERY;
7945
0
    }
7946
0
    code = createPhysiNode(pCxt, pLogicSubplan->pNode, pSubplan, &pSubplan->pNode);
7947
0
    if (TSDB_CODE_SUCCESS == code &&
7948
0
        (SUBPLAN_TYPE_SCAN == pSubplan->subplanType || SUBPLAN_TYPE_HSYSSCAN == pSubplan->subplanType) &&
7949
0
        QUERY_NODE_PHYSICAL_PLAN_HASH_INTERVAL == nodeType(pSubplan->pNode) &&
7950
0
        ((SIntervalPhysiNode*)pSubplan->pNode)->exInterval) {
7951
0
      pSubplan->delayFreeTask = true;
7952
0
    }
7953
0
    if (TSDB_CODE_SUCCESS == code && pSubplan->localBlockExchange) {
7954
0
      pSubplan->delayFreeTask = true;
7955
0
    }
7956
0
    if (TSDB_CODE_SUCCESS == code && !pCxt->pPlanCxt->topicQuery) {
7957
0
      code = createDataDispatcher(pCxt, pSubplan->pNode, pSubplan->processOneBlock,
7958
0
                                  pSubplan->localBlockExchange, &pSubplan->pDataSink);
7959
0
    }
7960
0
  }
7961
7962
0
  if (TSDB_CODE_SUCCESS == code) {
7963
0
    *pPhysiSubplan = pSubplan;
7964
0
  } else {
7965
0
    nodesDestroyNode((SNode*)pSubplan);
7966
0
  }
7967
7968
0
  return code;
7969
0
}
7970
7971
0
static int32_t makeQueryPhysiPlan(SPhysiPlanContext* pCxt, SQueryPlan** ppQueryPlan) {
7972
0
  SQueryPlan* pPlan = NULL;
7973
0
  int32_t     code = nodesMakeNode(QUERY_NODE_PHYSICAL_PLAN, (SNode**)&pPlan);
7974
0
  if (NULL == pPlan) {
7975
0
    return code;
7976
0
  }
7977
0
  pPlan->pSubplans = NULL;
7978
0
  code = nodesMakeList(&pPlan->pSubplans);
7979
0
  if (NULL == pPlan->pSubplans) {
7980
0
    nodesDestroyNode((SNode*)pPlan);
7981
0
    return code;
7982
0
  }
7983
0
  pPlan->queryId = pCxt->pPlanCxt->queryId;
7984
0
  *ppQueryPlan = pPlan;
7985
0
  return code;
7986
0
}
7987
7988
0
static int32_t pushSubplan(SPhysiPlanContext* pCxt, SNode* pSubplan, int32_t level, SNodeList* pSubplans) {
7989
0
  SNodeListNode* pGroup = NULL;
7990
0
  if (level >= LIST_LENGTH(pSubplans)) {
7991
0
    pGroup = NULL;
7992
0
    int32_t code = nodesMakeNode(QUERY_NODE_NODE_LIST, (SNode**)&pGroup);
7993
0
    if (NULL == pGroup) {
7994
0
      return code;
7995
0
    }
7996
0
    if (TSDB_CODE_SUCCESS != nodesListStrictAppend(pSubplans, (SNode*)pGroup)) {
7997
0
      return TSDB_CODE_OUT_OF_MEMORY;
7998
0
    }
7999
0
  } else {
8000
0
    pGroup = (SNodeListNode*)nodesListGetNode(pSubplans, level);
8001
0
  }
8002
0
  if (NULL == pGroup->pNodeList) {
8003
0
    int32_t code = nodesMakeList(&pGroup->pNodeList);
8004
0
    if (NULL == pGroup->pNodeList) {
8005
0
      return code;
8006
0
    }
8007
0
  }
8008
0
  return nodesListAppend(pGroup->pNodeList, (SNode*)pSubplan);
8009
0
}
8010
8011
0
static bool physiNodeRoutesSubplanGroup(const SPhysiNode* pNode, int32_t groupId) {
8012
0
  if (QUERY_NODE_PHYSICAL_PLAN_EXCHANGE == nodeType(pNode)) {
8013
0
    const SExchangePhysiNode* pExchange = (const SExchangePhysiNode*)pNode;
8014
0
    return groupId >= pExchange->srcStartGroupId && groupId <= pExchange->srcEndGroupId;
8015
0
  }
8016
0
  if (QUERY_NODE_PHYSICAL_PLAN_MERGE == nodeType(pNode)) {
8017
0
    const SMergePhysiNode* pMerge = (const SMergePhysiNode*)pNode;
8018
0
    return groupId >= pMerge->srcGroupId && groupId <= pMerge->srcEndGroupId;
8019
0
  }
8020
0
  return false;
8021
0
}
8022
8023
0
static bool physiSubplanEdgeInheritsMultiGroupJoin(const SPhysiNode* pNode, int32_t groupId, bool belowMultiGroupJoin) {
8024
0
  if (NULL == pNode) {
8025
0
    return false;
8026
0
  }
8027
8028
0
  if (physiNodeRoutesSubplanGroup(pNode, groupId)) {
8029
0
    return belowMultiGroupJoin;
8030
0
  }
8031
8032
0
  bool   childBelowMultiGroupJoin = belowMultiGroupJoin || (QUERY_NODE_PHYSICAL_PLAN_HASH_JOIN == nodeType(pNode) &&
8033
0
                                                          ((const SHashJoinPhysiNode*)pNode)->multiGroupJoin);
8034
0
  SNode* pChild = NULL;
8035
0
  FOREACH(pChild, pNode->pChildren) {
8036
0
    if (physiSubplanEdgeInheritsMultiGroupJoin((const SPhysiNode*)pChild, groupId, childBelowMultiGroupJoin)) {
8037
0
      return true;
8038
0
    }
8039
0
  }
8040
0
  return false;
8041
0
}
8042
8043
static int32_t buildPhysiPlan(SPhysiPlanContext* pCxt, SLogicSubplan* pLogicSubplan, SSubplan* pParent,
8044
0
                              SQueryPlan* pQueryPlan, bool inheritedMultiGroupJoin) {
8045
0
  SSubplan* pSubplan = NULL;
8046
0
  int32_t   code = createPhysiSubplan(pCxt, pLogicSubplan, &pSubplan);
8047
8048
0
  if (TSDB_CODE_SUCCESS == code) {
8049
0
    pSubplan->inheritedMultiGroupJoin = inheritedMultiGroupJoin;
8050
0
  }
8051
8052
0
  if (TSDB_CODE_SUCCESS == code) {
8053
0
    code = pushSubplan(pCxt, (SNode*)pSubplan, pLogicSubplan->level, pQueryPlan->pSubplans);
8054
0
    ++(pQueryPlan->numOfSubplans);
8055
0
  }
8056
8057
0
  if (TSDB_CODE_SUCCESS != code) {
8058
0
    nodesDestroyNode((SNode*)pSubplan);
8059
0
    return code;
8060
0
  }
8061
8062
0
  if (TSDB_CODE_SUCCESS == code && NULL != pParent) {
8063
0
    code = nodesListMakeAppend(&pParent->pChildren, (SNode*)pSubplan);
8064
0
    if (TSDB_CODE_SUCCESS == code) {
8065
0
      code = nodesListMakeAppend(&pSubplan->pParents, (SNode*)pParent);
8066
0
    }
8067
0
  }
8068
8069
0
  if (TSDB_CODE_SUCCESS == code) {
8070
0
    SNode* pChild = NULL;
8071
0
    FOREACH(pChild, pLogicSubplan->pChildren) {
8072
0
      SLogicSubplan* pLogicChild = (SLogicSubplan*)pChild;
8073
0
      bool           childInheritedMultiGroupJoin =
8074
0
          inheritedMultiGroupJoin ||
8075
0
          physiSubplanEdgeInheritsMultiGroupJoin(pSubplan->pNode, pLogicChild->id.groupId, false);
8076
0
      code = buildPhysiPlan(pCxt, pLogicChild, pSubplan, pQueryPlan, childInheritedMultiGroupJoin);
8077
0
      if (TSDB_CODE_SUCCESS != code) {
8078
0
        break;
8079
0
      }
8080
0
    }
8081
0
  }
8082
8083
0
  return code;
8084
0
}
8085
8086
0
static int32_t doCreatePhysiPlan(SPhysiPlanContext* pCxt, SQueryLogicPlan* pLogicPlan, SQueryPlan** pPhysiPlan) {
8087
0
  SQueryPlan* pPlan = NULL;
8088
0
  int32_t     code = makeQueryPhysiPlan(pCxt, &pPlan);
8089
0
  if (NULL == pPlan) {
8090
0
    return code;
8091
0
  }
8092
8093
0
  SNode* pSubplan = NULL;
8094
0
  FOREACH(pSubplan, pLogicPlan->pTopSubplans) {
8095
0
    code = buildPhysiPlan(pCxt, (SLogicSubplan*)pSubplan, NULL, pPlan, false);
8096
0
    if (TSDB_CODE_SUCCESS != code) {
8097
0
      break;
8098
0
    }
8099
0
  }
8100
8101
0
  if (TSDB_CODE_SUCCESS == code) {
8102
    // * Propagate hasFederatedScan / hasScan flags to the plan output
8103
0
    pPlan->hasFederatedScan = pCxt->hasFederatedScan;
8104
0
    pPlan->hasScan          = pCxt->pPlanCxt->hasScan;
8105
0
    *pPhysiPlan = pPlan;
8106
0
  } else {
8107
0
    nodesDestroyNode((SNode*)pPlan);
8108
0
  }
8109
8110
0
  return code;
8111
0
}
8112
8113
0
static void destoryLocationHash(void* p) {
8114
0
  destroySlotIndexHash(*(SHashObj**)p);
8115
0
}
8116
8117
0
static void destoryPhysiPlanContext(SPhysiPlanContext* pCxt) {
8118
0
  taosArrayDestroyEx(pCxt->pLocationHelper, destoryLocationHash);
8119
0
  taosArrayDestroyEx(pCxt->pProjIdxLocHelper, destoryLocationHash);
8120
0
}
8121
8122
0
static void setExplainInfo(SPlanContext* pCxt, SQueryPlan* pPlan) {
8123
0
  if (QUERY_NODE_EXPLAIN_STMT == nodeType(pCxt->pAstRoot)) {
8124
0
    SExplainStmt* pStmt = (SExplainStmt*)pCxt->pAstRoot;
8125
0
    pPlan->explainInfo.mode = pStmt->analyze ? EXPLAIN_MODE_ANALYZE : EXPLAIN_MODE_STATIC;
8126
0
    pPlan->explainInfo.verbose = pStmt->pOptions->verbose;
8127
0
    pPlan->explainInfo.ratio = pStmt->pOptions->ratio;
8128
0
    pPlan->explainInfo.tz = pCxt->timezone;
8129
0
  } else {
8130
0
    pPlan->explainInfo.mode = EXPLAIN_MODE_DISABLE;
8131
0
  }
8132
0
}
8133
8134
0
static int32_t setExecNodeList(SPhysiPlanContext* pCxt, SArray* pExecNodeList) {
8135
0
  int32_t code = 0;
8136
0
  if (NULL == pExecNodeList) {
8137
0
    return code;
8138
0
  }
8139
8140
0
  if (IS_SYS_SCAN(pCxt->pPlanCxt->sysScanFlag) || !pCxt->pPlanCxt->hasScan) {
8141
0
    SQueryNodeLoad node = {.addr = {.nodeId = MNODE_HANDLE, .epSet = pCxt->pPlanCxt->mgmtEpSet}, .load = 0};
8142
0
    if (NULL == taosArrayPush(pExecNodeList, &node)) code = terrno;
8143
0
  }
8144
0
  return code;
8145
0
}
8146
8147
0
int32_t createPhysiPlan(SPlanContext* pCxt, SQueryLogicPlan* pLogicPlan, SQueryPlan** pPlan, SArray* pExecNodeList) {
8148
0
  SPhysiPlanContext cxt = {.pPlanCxt = pCxt,
8149
0
                           .errCode = TSDB_CODE_SUCCESS,
8150
0
                           .nextDataBlockId = 0,
8151
0
                           .pLocationHelper = taosArrayInit(32, POINTER_BYTES),
8152
0
                           .pProjIdxLocHelper = taosArrayInit(32, POINTER_BYTES),
8153
0
                           };
8154
0
  if (NULL == cxt.pLocationHelper || !cxt.pProjIdxLocHelper) {
8155
0
    taosArrayDestroy(cxt.pLocationHelper);
8156
0
    taosArrayDestroy(cxt.pProjIdxLocHelper);
8157
0
    return terrno;
8158
0
  }
8159
8160
0
  int32_t code = doCreatePhysiPlan(&cxt, pLogicPlan, pPlan);
8161
0
  if (TSDB_CODE_SUCCESS == code) {
8162
0
    setExplainInfo(pCxt, *pPlan);
8163
0
    code = setExecNodeList(&cxt, pExecNodeList);
8164
0
  }
8165
8166
0
  destoryPhysiPlanContext(&cxt);
8167
0
  return code;
8168
0
}