@Override
  public List<PlanNode> visitProject(ProjectNode node, PlanContext context) {
    List<PlanNode> childrenNodes = node.getChild().accept(this, context);
    OrderingScheme childOrdering = nodeOrderingMap.get(childrenNodes.get(0).getPlanNodeId());
    boolean containAllSortItem = false;
    if (childOrdering != null) {
      // the column used for order by has been pruned, we can't copy this node to sub nodeTrees.
      containAllSortItem =
          ImmutableSet.copyOf(node.getOutputSymbols()).containsAll(childOrdering.getOrderBy());
    }
    if (childrenNodes.size() == 1) {
      PlanNode child = childrenNodes.get(0);
      if (containAllSortItem) {
        nodeOrderingMap.put(node.getPlanNodeId(), childOrdering);
      }

      // Now the join implement but CROSS is MergeSortJoin, so it can keep order
      if (child instanceof JoinNode) {
        JoinNode joinNode = (JoinNode) child;

        // We only process FULL Join here, other type will be processed in visitJoinNode()
        if (joinNode.getJoinType() == JoinNode.JoinType.FULL
            && !joinNode.getAsofCriteria().isPresent()) {
          Map<Symbol, Expression> assignmentsMap = node.getAssignments().getMap();
          // If these Coalesces are all appear in ProjectNode, the ProjectNode is ordered
          int coalescesSize = joinNode.getCriteria().size();

          // We use map to memorize Symbol of according Coalesce, use linked to avoid twice query of
          // this Map when constructOrderingSchema
          Map<Expression, Symbol> orderedCoalesces = new LinkedHashMap<>(coalescesSize);
          for (JoinNode.EquiJoinClause clause : joinNode.getCriteria()) {
            orderedCoalesces.put(
                new CoalesceExpression(
                    ImmutableList.of(
                        clause.getLeft().toSymbolReference(),
                        clause.getRight().toSymbolReference())),
                null);
          }

          for (Map.Entry<Symbol, Expression> assignment : assignmentsMap.entrySet()) {
            if (orderedCoalesces.containsKey(assignment.getValue())) {
              coalescesSize--;
              orderedCoalesces.put(assignment.getValue(), assignment.getKey());
            }
          }

          // All Coalesces appear in ProjectNode
          if (coalescesSize == 0) {
            nodeOrderingMap.put(
                node.getPlanNodeId(),
                constructOrderingSchema(new ArrayList<>(orderedCoalesces.values())));
          }
        }
      }

      node.setChild(child);
      return Collections.singletonList(node);
    }

    boolean containsDiff =
        node.getAssignments().getMap().values().stream()
            .anyMatch(PushPredicateIntoTableScan::containsDiffFunction);
    if (containsDiff) {
      if (containAllSortItem) {
        nodeOrderingMap.put(node.getPlanNodeId(), childOrdering);
      }
      node.setChild(mergeChildrenViaCollectOrMergeSort(childOrdering, childrenNodes));
      return Collections.singletonList(node);
    }

    List<PlanNode> resultNodeList = new ArrayList<>(childrenNodes.size());
    for (PlanNode child : childrenNodes) {
      ProjectNode subProjectNode =
          new ProjectNode(queryId.genPlanNodeId(), child, node.getAssignments());
      resultNodeList.add(subProjectNode);
      if (containAllSortItem) {
        nodeOrderingMap.put(subProjectNode.getPlanNodeId(), childOrdering);
      }
    }
    return resultNodeList;
  }
This class is used to generate distributed plan for table model.
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java