private void generateActualTasks(HiveConf conf, List<Task<?>> resTsks,
      long trgtSize, long avgConditionSize, Task<?> mvTask,
      Task<?> mrTask, Task<?> mrAndMvTask, Path dirPath,
      FileSystem inpFs, ConditionalResolverMergeFilesCtx ctx, MapWork work, int dpLbLevel,
      boolean manifestFilePresent, HiveStorageHandler storageHandler)
      throws IOException, ClassNotFoundException {
    DynamicPartitionCtx dpCtx = ctx.getDPCtx();
    List<FileStatus> statusList;
    Map<FileStatus, List<FileStatus>> parentDirToFile = new HashMap<>();
    boolean useCustomStorageHandler = storageHandler != null && storageHandler.supportsMergeFiles();
    MergeTaskProperties mergeProperties = useCustomStorageHandler ?
            storageHandler.getMergeTaskProperties(ctx.getTaskProperties()) : null;
    if (manifestFilePresent) {
      // Get the list of files from manifest file.
      List<FileStatus> fileStatuses = getManifestFilePaths(conf, dirPath);
      // Setup the work to include all the files present in the manifest.
      setupWorkWhenUsingManifestFile(work, fileStatuses, dirPath, false);
      parentDirToFile = getParentDirToFileMap(inpFs, fileStatuses);
      statusList = Lists.newArrayList(parentDirToFile.keySet());
    } else if (useCustomStorageHandler) {
      List<FileStatus> fileStatuses = storageHandler.getMergeTaskInputFiles(ctx.getTaskProperties());
      setupWorkWithCustomHandler(work, dirPath, mergeProperties);
      parentDirToFile = getParentDirToFileMap(inpFs, fileStatuses);
      statusList = Lists.newArrayList(parentDirToFile.keySet());
    } else {
      statusList = HiveStatsUtils.getFileStatusRecurse(dirPath, dpLbLevel, inpFs);
    }
    FileStatus[] status = statusList.toArray(new FileStatus[statusList.size()]);

    // cleanup pathToPartitionInfo
    Map<Path, PartitionDesc> ptpi = work.getPathToPartitionInfo();
    assert ptpi.size() == 1;
    Path path = ptpi.keySet().iterator().next();
    PartitionDesc partDesc = ptpi.get(path);
    TableDesc tblDesc = partDesc.getTableDesc();
    Utilities.FILE_OP_LOGGER.debug("merge resolver removing " + path);
    work.removePathToPartitionInfo(path); // the root path is not useful anymore

    // cleanup pathToAliases
    Map<Path, List<String>> pta = work.getPathToAliases();
    assert pta.size() == 1;
    path = pta.keySet().iterator().next();
    List<String> aliases = pta.get(path);
    work.removePathToAlias(path); // the root path is not useful anymore

    // populate pathToPartitionInfo and pathToAliases w/ DP paths
    long totalSize = 0;
    boolean doMerge = false;
    // list of paths that don't need to merge but need to move to the dest location
    List<Path> toMove = new ArrayList<>();
    List<Path> toMerge = new ArrayList<>();
    for (int i = 0; i < status.length; ++i) {
      long len;
      if (manifestFilePresent || useCustomStorageHandler) {
        len = getMergeSize(parentDirToFile.get(status[i]), avgConditionSize);
      } else {
        len = getMergeSize(inpFs, status[i].getPath(), avgConditionSize);
      }
      if (len >= 0) {
        doMerge = true;
        totalSize += len;
        PartitionDesc pDesc = (dpCtx != null) ? generateDPFullPartSpec(dpCtx, status, tblDesc, i)
            : partDesc;
        if (pDesc == null) {
          Utilities.FILE_OP_LOGGER.warn("merger ignoring invalid DP path " + status[i].getPath());
          continue;
        }
        Utilities.FILE_OP_LOGGER.debug("merge resolver will merge " + status[i].getPath());
        work.resolveDynamicPartitionStoredAsSubDirsMerge(conf, status[i].getPath(), tblDesc,
            aliases, pDesc);
        // Do not add input file since its already added when the manifest file is present.
        if (manifestFilePresent || useCustomStorageHandler) {
          toMerge.addAll(parentDirToFile.get(status[i])
              .stream().map(FileStatus::getPath).collect(Collectors.toList()));
        } else {
          toMerge.add(status[i].getPath());
        }
      } else {
        Utilities.FILE_OP_LOGGER.debug("merge resolver will move " + status[i].getPath());

        toMove.add(status[i].getPath());
      }
    }
    if (doMerge) {
      // Set paths appropriately.
      if (work.getInputPaths() != null && !work.getInputPaths().isEmpty()) {
        toMerge.addAll(work.getInputPaths());
      }
      work.setInputPaths(toMerge);
      // add the merge MR job
      setupMapRedWork(conf, work, trgtSize, totalSize);

      // add the move task for those partitions that do not need merging
      if (toMove.size() > 0) {
        // Note: this path should be specific to concatenate; never executed in a select query.
        // modify the existing move task as it is already in the candidate running tasks

        // running the MoveTask and MR task in parallel may
        // cause the mvTask write to /ds=1 and MR task write
        // to /ds=1_1 for the same partition.
        // make the MoveTask as the child of the MR Task
        resTsks.add(mrAndMvTask);

        // Originally the mvTask and the child move task of the mrAndMvTask contain the same
        // MoveWork object.
        // If the blobstore optimizations are on and the input/output paths are merged
        // in the move only MoveWork, the mvTask and the child move task of the mrAndMvTask
        // will contain different MoveWork objects, which causes problems.
        // Not just in this case, but also in general the child move task of the mrAndMvTask should
        // be used, because that is the correct move task for the "merge and move" use case.
        Task<?> mergeAndMoveMoveTask = mrAndMvTask.getChildTasks().get(0);
        MoveWork mvWork = (MoveWork) mergeAndMoveMoveTask.getWork();

        LoadFileDesc lfd = mvWork.getLoadFileWork();

        Path targetDir = lfd.getTargetDir();
        List<Path> targetDirs = new ArrayList<Path>(toMove.size());

        for (int i = 0; i < toMove.size(); i++) {
          // Here directly the path name is used, instead of the uri because the uri contains the
          // serialized version of the path. For dynamic partition, we need the non serialized
          // version of the path as this value is used directly as partition name to create the partition.
          // For example, if the dp name is "part=2022-01-16 04:35:56.732" then the uri
          // will contain "part=2022-01-162022-01-16%2004%253A35%253A56.732". When we convert it to
          // partition name, it will come as "part=2022-01-16 04%3A35%3A56.732". But the path will have
          // value "part=2022-01-16 04%3A35%3A56.732", which will get converted to proper name by
          // function escapePathName.
          String[] moveStrSplits = toMove.get(i).toString().split(Path.SEPARATOR);
          int dpIndex = moveStrSplits.length - dpLbLevel;
          Path target = targetDir;
          while (dpIndex < moveStrSplits.length) {
            target = new Path(target, moveStrSplits[dpIndex]);
            dpIndex++;
          }

          targetDirs.add(target);
        }

        mvWork.setLoadFileWork(null);
        mvWork.setLoadTableWork(null);
        if (!useCustomStorageHandler) {
          LoadMultiFilesDesc lmfd = new LoadMultiFilesDesc(toMove,
                  targetDirs, lfd.getIsDfsDir(), lfd.getColumns(), lfd.getColumnTypes());
          mvWork.setMultiFilesDesc(lmfd);
        }
      } else {
        resTsks.add(mrTask);
      }
    } else { // add the move task
      resTsks.add(mvTask);
    }
  }
This method generates actual task for conditional tasks. It could be
1. move task only
2. merge task only
3. merge task followed by a move task.
It used to be true for dynamic partition only since static partition doesn't have #3.
It changes w/ list bucketing. Static partition has #3 since it has sub-directories.
For example, if a static partition is defined as skewed and stored-as-directories,
instead of all files in one directory, it will create a sub-dir per skewed value plus
default directory. So #3 is required for static partition.
So, we move it to a method so that it can be used by both SP and DP.
@param conf
@param resTsks
@param trgtSize
@param avgConditionSize
@param mvTask
@param mrTask
@param mrAndMvTask
@param dirPath
@param inpFs
@param ctx
@param work
@param dpLbLevel
@throws IOException
ql/src/java/org/apache/hadoop/hive/ql/plan/ConditionalResolverMergeFiles.java