@SuppressWarnings("unchecked")
  @Override
  public Object process(Node nd, Stack<Node> stack, NodeProcessorCtx procContext,
      Object... nodeOutputs) throws SemanticException {

    GenTezProcContext context = (GenTezProcContext) procContext;

    TableScanOperator tableScan = (TableScanOperator) nd;

    ParseContext parseContext = context.parseContext;
    Table table = tableScan.getConf().getTableMetadata();
    Class<? extends InputFormat> inputFormat = table.getInputFormatClass();

    if (parseContext.getQueryProperties().isAnalyzeCommand()) {

      assert tableScan.getChildOperators() == null || tableScan.getChildOperators().size() == 0;

      String alias = null;
      for (String a : parseContext.getTopOps().keySet()) {
        if (tableScan == parseContext.getTopOps().get(a)) {
          alias = a;
        }
      }

      assert alias != null;
      TezWork tezWork = context.currentTask.getWork();
      if (BasicStatsNoJobTask.canUseBasicStats(table, inputFormat)) {
        // For ORC, Parquet and Iceberg tables, all the following statements are the same
        // ANALYZE TABLE T [PARTITION (...)] COMPUTE STATISTICS
        // ANALYZE TABLE T [PARTITION (...)] COMPUTE STATISTICS noscan;

        // There will not be any Tez job above this task
        StatsWork statWork = new StatsWork(table, parseContext.getConf());
        statWork.setFooterScan();

        // If partition is specified, get pruned partition list
        Set<Partition> confirmedParts = GenMapRedUtils.getConfirmedPartitionsForScan(tableScan);
        if (confirmedParts.size() > 0) {
          List<String> partCols = GenMapRedUtils.getPartitionColumns(tableScan);
          PrunedPartitionList partList = new PrunedPartitionList(table, confirmedParts, partCols, false);
          statWork.addInputPartitions(partList.getPartitions());
        }
        Task<StatsWork> snjTask = TaskFactory.get(statWork);
        snjTask.setParentTasks(null);
        context.rootTasks.remove(context.currentTask);
        context.rootTasks.add(snjTask);
        return true;
      } else {

        // ANALYZE TABLE T [PARTITION (...)] COMPUTE STATISTICS;
        // The plan consists of a simple TezTask followed by a StatsTask.
        // The Tez task is just a simple TableScanOperator

        BasicStatsWork basicStatsWork = new BasicStatsWork(table.getTableSpec());
        basicStatsWork.setIsExplicitAnalyze(true);
        basicStatsWork.setNoScanAnalyzeCommand(parseContext.getQueryProperties().isNoScanAnalyzeCommand());
        StatsWork columnStatsWork = new StatsWork(table, basicStatsWork, parseContext.getConf());
        columnStatsWork.collectStatsFromAggregator(tableScan.getConf());

        columnStatsWork.setSourceTask(context.currentTask);
        Task<StatsWork> statsTask = TaskFactory.get(columnStatsWork);
        context.currentTask.addDependentTask(statsTask);

        // ANALYZE TABLE T [PARTITION (...)] COMPUTE STATISTICS noscan;
        // The plan consists of a StatsTask only.
        if (parseContext.getQueryProperties().isNoScanAnalyzeCommand()) {
          statsTask.setParentTasks(null);
          context.rootTasks.remove(context.currentTask);
          context.rootTasks.add(statsTask);
        }

        // NOTE: here we should use the new partition predicate pushdown API to
        // get a list of pruned list,
        // and pass it to setTaskPlan as the last parameter
        Set<Partition> confirmedPartns = GenMapRedUtils.getConfirmedPartitionsForScan(tableScan);
        PrunedPartitionList partitions = null;
        if (confirmedPartns.size() > 0) {
          List<String> partCols = GenMapRedUtils.getPartitionColumns(tableScan);
          partitions = new PrunedPartitionList(table, confirmedPartns, partCols, false);
        }

        MapWork w = utils.createMapWork(context, tableScan, tezWork, partitions);
        w.setGatheringStats(true);

        return true;
      }
    }

    return null;
  }
Injecting the utils in the constructor facilitates testing
ql/src/java/org/apache/hadoop/hive/ql/parse/ProcessAnalyzeTable.java