public PipelineMemoryEstimator constructPipelineMemoryEstimator(
      final Operator operation,
      @Nullable final PlanNodeId parentPlanNodeId,
      final PlanNode root,
      final int dependencyPipelineIndex) {
    PipelineMemoryEstimator currentPipelineMemoryEstimator =
        PipelineMemoryEstimatorFactory.createPipelineMemoryEstimator(
            operation, root, dependencyPipelineIndex);
    // As OperatorTreeGenerator traverse the tree in a post-order way, all the children of current
    // pipeline have been recorded in the map.
    List<PipelineMemoryEstimator> childrenMemoryEstimators =
        parentPlanNodeIdToMemoryEstimator.get(root.getPlanNodeId());
    if (childrenMemoryEstimators != null) {
      currentPipelineMemoryEstimator.addChildren(childrenMemoryEstimators);
    }
    if (parentPlanNodeId != null) {
      parentPlanNodeIdToMemoryEstimator
          .computeIfAbsent(parentPlanNodeId, k -> new LinkedList<>())
          .add(currentPipelineMemoryEstimator);
    }
    return currentPipelineMemoryEstimator;
  }
Each time we construct a pipeline, we should also construct the memory estimator for the
pipeline.

@param operation the root operator of the pipeline
@param parentPlanNodeId the parent plan node id of the root of the pipeline
@param root the root node of the pipeline
@param dependencyPipelineIndex the index of the dependency pipeline, -1 if no dependency
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlanContext.java