public List<PipelineDriverFactory> plan(
      PlanNode plan,
      TypeProvider types,
      FragmentInstanceContext instanceContext,
      ISchemaRegion schemaRegion)
      throws MemoryNotEnoughException {
    if (Objects.isNull(plan)) {
      throw new IoTDBRuntimeException(
          "The planNode is null during local execution, maybe caused by closing of the current dataNode",
          TSStatusCode.CLOSE_OPERATION_ERROR.getStatusCode());
    }
    LocalExecutionPlanContext context =
        new LocalExecutionPlanContext(types, instanceContext, schemaRegion);

    Operator root = generateOperator(instanceContext, context, plan);

    PipelineMemoryEstimator memoryEstimator =
        context.constructPipelineMemoryEstimator(root, null, plan, -1);
    // set the map to null for gc
    context.invalidateParentPlanNodeIdToMemoryEstimator();

    // check whether current free memory is enough to execute current query
    checkMemory(memoryEstimator, instanceContext.getStateMachine());

    context.addPipelineDriverFactory(root, context.getDriverContext(), 0);

    // set maxBytes one SourceHandle can reserve after visiting the whole tree
    context.setMaxBytesOneHandleCanReserve();

    return context.getPipelineDriverFactories();
  }
Used to plan a fragment instance. One fragment instance could be split into multiple pipelines so
that a fragment instance could be run in parallel, and thus we can take full advantages of
multi-cores.
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlanner.java