private static GBInfo getGBInfo(HiveAggregate aggRel, OpAttr inputOpAf, HiveConf hc) throws SemanticException {
    GBInfo gbInfo = new GBInfo();

    // 0. Collect AggRel output col Names
    gbInfo.outputColNames.addAll(aggRel.getRowType().getFieldNames());

    // 1. Collect GB Keys
    RelNode aggInputRel = aggRel.getInput();
    ExprNodeConverter exprConv = new ExprNodeConverter(inputOpAf.tabAlias,
        aggInputRel.getRowType(), new HashSet<Integer>(), aggRel.getCluster().getTypeFactory(),
        true);

    ExprNodeDesc tmpExprNodeDesc;
    for (int i : aggRel.getGroupSet()) {
      RexInputRef iRef = new RexInputRef(i, aggInputRel.getRowType().getFieldList()
          .get(i).getType());
      tmpExprNodeDesc = iRef.accept(exprConv);
      gbInfo.gbKeys.add(tmpExprNodeDesc);
      gbInfo.gbKeyColNamesInInput.add(aggInputRel.getRowType().getFieldNames().get(i));
      gbInfo.gbKeyTypes.add(tmpExprNodeDesc.getTypeInfo());
    }

    // 2. Collect Grouping Set info
    if (aggRel.getGroupType() != Group.SIMPLE) {
      // 2.1 Translate Grouping set col bitset
      ImmutableList<ImmutableBitSet> lstGrpSet = aggRel.getGroupSets();
      long bitmap = 0;
      for (ImmutableBitSet grpSet : lstGrpSet) {
        bitmap = 0;
        for (Integer bitIdx : grpSet.asList()) {
          bitmap = SemanticAnalyzer.setBit(bitmap, bitIdx);
        }
        gbInfo.grpSets.add(bitmap);
      }
      Collections.sort(gbInfo.grpSets);

      // 2.2 Check if GRpSet require additional MR Job
      gbInfo.grpSetRqrAdditionalMRJob = gbInfo.grpSets.size() > hc
          .getIntVar(HiveConf.ConfVars.HIVE_NEW_JOB_GROUPING_SET_CARDINALITY);

      // 2.3 Check if GROUPING_ID needs to be projected out
      if (!aggRel.getAggCallList().isEmpty()
          && (aggRel.getAggCallList().get(aggRel.getAggCallList().size() - 1).getAggregation() == HiveGroupingID.INSTANCE)) {
        gbInfo.grpIdFunctionNeeded = true;
      }
    }

    // 3. Walk through UDAF & Collect Distinct Info
    Set<Integer> distinctRefs = new HashSet<Integer>();
    Map<Integer, Integer> distParamInRefsToOutputPos = new HashMap<Integer, Integer>();
    for (AggregateCall aggCall : aggRel.getAggCallList()) {
      if ((aggCall.getAggregation() == HiveGroupingID.INSTANCE) || !aggCall.isDistinct()) {
        continue;
      }

      List<Integer> argLst = new ArrayList<Integer>(aggCall.getArgList());
      List<String> argNames = HiveCalciteUtil.getFieldNames(argLst, aggInputRel);
      ExprNodeDesc distinctExpr;
      for (int i = 0; i < argLst.size(); i++) {
        if (!distinctRefs.contains(argLst.get(i))) {
          distinctRefs.add(argLst.get(i));
          distinctExpr = HiveCalciteUtil.getExprNode(argLst.get(i), aggInputRel, exprConv);
          // Only distinct nodes that are NOT part of the key should be added to distExprNodes
          if (ExprNodeDescUtils.indexOf(distinctExpr, gbInfo.gbKeys) < 0) {
            distParamInRefsToOutputPos.put(argLst.get(i), gbInfo.distExprNodes.size());
            gbInfo.distExprNodes.add(distinctExpr);
            gbInfo.distExprNames.add(argNames.get(i));
            gbInfo.distExprTypes.add(distinctExpr.getTypeInfo());
          }
        }
      }
    }

    // 4. Walk through UDAF & Collect UDAF Info
    Set<Integer> deDupedNonDistIrefsSet = new HashSet<Integer>();
    for (AggregateCall aggCall : aggRel.getAggCallList()) {
      if (aggCall.getAggregation() == HiveGroupingID.INSTANCE) {
        continue;
      }

      UDAFAttrs udafAttrs = new UDAFAttrs();
      List<ExprNodeDesc> argExps = HiveCalciteUtil.getExprNodes(aggCall.getArgList(), aggInputRel,
          inputOpAf.tabAlias);
      udafAttrs.udafParams.addAll(argExps);
      udafAttrs.udafName = aggCall.getAggregation().getName();
      udafAttrs.isDistinctUDAF = aggCall.isDistinct();
      List<Integer> argLst = new ArrayList<Integer>(aggCall.getArgList());
      List<Integer> distColIndicesOfUDAF = new ArrayList<Integer>();
      List<Integer> distUDAFParamsIndxInDistExprs = new ArrayList<Integer>();
      for (int i = 0; i < argLst.size(); i++) {
        // NOTE: distinct expr can be part of of GB key
        if (udafAttrs.isDistinctUDAF) {
          ExprNodeDesc argExpr = argExps.get(i);
          int found = ExprNodeDescUtils.indexOf(argExpr, gbInfo.gbKeys);
          distColIndicesOfUDAF.add(found < 0 ? distParamInRefsToOutputPos.get(argLst.get(i)) + gbInfo.gbKeys.size() +
              (gbInfo.grpSets.size() > 0 ? 1 : 0) : found);
          distUDAFParamsIndxInDistExprs.add(distParamInRefsToOutputPos.get(argLst.get(i)));
        } else {
          // TODO: this seems wrong (following what Hive Regular does)
          if (!distParamInRefsToOutputPos.containsKey(argLst.get(i))
              && !deDupedNonDistIrefsSet.contains(argLst.get(i))) {
            deDupedNonDistIrefsSet.add(argLst.get(i));
            gbInfo.deDupedNonDistIrefs.add(udafAttrs.udafParams.get(i));
          }
        }
      }

      if (udafAttrs.isDistinctUDAF) {
        gbInfo.containsDistinctAggr = true;

        udafAttrs.udafParamsIndxInGBInfoDistExprs = distUDAFParamsIndxInDistExprs;
        gbInfo.distColIndices.add(distColIndicesOfUDAF);
      }

      // special handling for count, similar to PlanModifierForASTConv::replaceEmptyGroupAggr()
      udafAttrs.udafEvaluator = SemanticAnalyzer.getGenericUDAFEvaluator(udafAttrs.udafName,
          new ArrayList<ExprNodeDesc>(udafAttrs.udafParams), new ASTNode(),
          udafAttrs.isDistinctUDAF, udafAttrs.udafParams.size() == 0 &&
          "count".equalsIgnoreCase(udafAttrs.udafName) ? true : false);
      gbInfo.udafAttrs.add(udafAttrs);
    }

    // 4. Gather GB Memory threshold
    gbInfo.groupByMemoryUsage = HiveConf.getFloatVar(hc, HiveConf.ConfVars.HIVE_MAP_AGGR_HASH_MEMORY);
    gbInfo.memoryThreshold = HiveConf.getFloatVar(hc, HiveConf.ConfVars.HIVE_MAP_AGGR_MEMORY_THRESHOLD);
    gbInfo.minReductionHashAggr = HiveConf.getFloatVar(hc, HiveConf.ConfVars.HIVE_MAP_AGGR_HASH_MIN_REDUCTION);
    gbInfo.minReductionHashAggrLowerBound =
            HiveConf.getFloatVar(hc, HiveConf.ConfVars.HIVE_MAP_AGGR_HASH_MIN_REDUCTION_LOWER_BOUND);

    // 5. Gather GB Physical pipeline (based on user config & Grping Sets size)
    gbInfo.gbPhysicalPipelineMode = getAggOPMode(hc, gbInfo);
    gbInfo.defaultNullOrder = NullOrdering.defaultNullOrder(hc);

    return gbInfo;
  }
TODO:<br>
1. Change the output col/ExprNodeColumn names to external names.<br>
2. Verify if we need to use the "KEY."/"VALUE." in RS cols; switch to
external names if possible.<br>
3. In ExprNode &amp; in ColumnInfo the tableAlias/VirtualColumn is specified
differently for different GB/RS in pipeline. Remove the different treatments.
4. VirtualColMap needs to be maintained
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/translator/opconventer/HiveGBOpConvUtil.java