public static MapJoinDesc getMapJoinDesc(HiveConf hconf,
      JoinOperator op, boolean leftInputJoin, String[] baseSrc, List<String> mapAliases,
      int mapJoinPos, boolean noCheckOuterJoin, boolean adjustParentsChildren) throws SemanticException {
    JoinDesc desc = op.getConf();
    JoinCondDesc[] condns = desc.getConds();
    Byte[] tagOrder = desc.getTagOrder();

    // outer join cannot be performed on a table which is being cached
    if (!noCheckOuterJoin) {
      if (checkMapJoin(mapJoinPos, condns) < 0) {
        throw new SemanticException(ErrorMsg.NO_OUTER_MAPJOIN.getMsg());
      }
    }

    Map<String, ExprNodeDesc> colExprMap = op.getColumnExprMap();
    List<ColumnInfo> schema = new ArrayList<ColumnInfo>(op.getSchema().getSignature());
    Map<Byte, List<ExprNodeDesc>> valueExprs = op.getConf().getExprs();
    Map<Byte, List<ExprNodeDesc>> newValueExprs = new HashMap<Byte, List<ExprNodeDesc>>();

    Pair<List<ReduceSinkOperator>, Map<Byte, List<ExprNodeDesc>>> pair = getKeys(leftInputJoin, baseSrc, op);
    List<ReduceSinkOperator> oldReduceSinkParentOps = pair.getLeft();
    for (Map.Entry<Byte, List<ExprNodeDesc>> entry : valueExprs.entrySet()) {
      byte tag = entry.getKey();
      Operator<?> terminal = oldReduceSinkParentOps.get(tag);

      List<ExprNodeDesc> values = entry.getValue();
      List<ExprNodeDesc> newValues = ExprNodeDescUtils.backtrack(values, op, terminal);
      newValueExprs.put(tag, newValues);
      for (int i = 0; i < schema.size(); i++) {
        ColumnInfo column = schema.get(i);
        if (column == null) {
          continue;
        }
        ExprNodeDesc expr = colExprMap.get(column.getInternalName());
        int index = ExprNodeDescUtils.indexOf(expr, values);
        if (index >= 0) {
          schema.set(i, null);
          if (adjustParentsChildren) {
            // Since we remove reduce sink parents, replace original expressions
            colExprMap.put(column.getInternalName(), newValues.get(index));
          }
        }
      }
    }

    // rewrite value index for mapjoin
    Map<Byte, int[]> valueIndices = new HashMap<Byte, int[]>();

    // get the join keys from old parent ReduceSink operators
    Map<Byte, List<ExprNodeDesc>> keyExprMap = pair.getRight();

    if (!adjustParentsChildren) {
      // Since we did not remove reduce sink parents, keep the original value expressions
      newValueExprs = valueExprs;

      // Join key exprs are represented in terms of the original table columns,
      // we need to convert these to the generated column names we can see in the Join operator
      Map<Byte, List<ExprNodeDesc>> newKeyExprMap = new HashMap<Byte, List<ExprNodeDesc>>();
      for (Map.Entry<Byte, List<ExprNodeDesc>> mapEntry : keyExprMap.entrySet()) {
        Byte pos = mapEntry.getKey();
        ReduceSinkOperator rsParent = oldReduceSinkParentOps.get(pos.byteValue());
        List<ExprNodeDesc> keyExprList =
            ExprNodeDescUtils.resolveJoinKeysAsRSColumns(mapEntry.getValue(), rsParent);
        if (keyExprList == null) {
          LOG.warn("Error resolving join keys {} in {} {}", mapEntry.getValue(), rsParent, rsParent.getColumnExprMap());
          return null;
        }
        newKeyExprMap.put(pos, keyExprList);
      }
      keyExprMap = newKeyExprMap;
    }

    // construct valueTableDescs and valueFilteredTableDescs
    List<TableDesc> valueTableDescs = new ArrayList<TableDesc>();
    List<TableDesc> valueFilteredTableDescs = new ArrayList<TableDesc>();
    int[][] filterMap = desc.getFilterMap();
    for (byte pos = 0; pos < op.getParentOperators().size(); pos++) {
      List<ExprNodeDesc> valueCols = newValueExprs.get(pos);
      if (pos != mapJoinPos) {
        // remove values in key exprs for value table schema
        // value expression for hashsink will be modified in
        // LocalMapJoinProcessor
        int[] valueIndex = new int[valueCols.size()];
        List<ExprNodeDesc> valueColsInValueExpr = new ArrayList<ExprNodeDesc>();
        for (int i = 0; i < valueIndex.length; i++) {
          ExprNodeDesc expr = valueCols.get(i);
          int kindex = ExprNodeDescUtils.indexOf(expr, keyExprMap.get(pos));
          if (kindex >= 0) {
            valueIndex[i] = kindex;
          } else {
            valueIndex[i] = -valueColsInValueExpr.size() - 1;
            valueColsInValueExpr.add(expr);
          }
        }
        if (needValueIndex(valueIndex)) {
          valueIndices.put(pos, valueIndex);
        }
        valueCols = valueColsInValueExpr;
      }
      // deep copy expr node desc
      List<ExprNodeDesc> valueFilteredCols = ExprNodeDescUtils.clone(valueCols);
      if (filterMap != null && filterMap[pos] != null && pos != mapJoinPos) {
        ExprNodeColumnDesc isFilterDesc =
            new ExprNodeColumnDesc(
                TypeInfoFactory.getPrimitiveTypeInfo(serdeConstants.SMALLINT_TYPE_NAME), "filter",
                "filter", false);
        valueFilteredCols.add(isFilterDesc);
      }

      TableDesc valueTableDesc =
          PlanUtils.getMapJoinValueTableDesc(PlanUtils.getFieldSchemasFromColumnList(valueCols,
              "mapjoinvalue"));
      TableDesc valueFilteredTableDesc =
          PlanUtils.getMapJoinValueTableDesc(PlanUtils.getFieldSchemasFromColumnList(
              valueFilteredCols, "mapjoinvalue"));

      valueTableDescs.add(valueTableDesc);
      valueFilteredTableDescs.add(valueFilteredTableDesc);
    }

    Map<Byte, List<ExprNodeDesc>> filters = desc.getFilters();
    if(adjustParentsChildren) {
      // backtrack and update filter expressions only if RS is to be removed
      Map<Byte, List<ExprNodeDesc>> newFilters = new HashMap<Byte, List<ExprNodeDesc>>();
      for (Map.Entry<Byte, List<ExprNodeDesc>> entry : filters.entrySet()) {
        byte srcTag = entry.getKey();
        List<ExprNodeDesc> filter = entry.getValue();

        Operator<?> terminal = oldReduceSinkParentOps.get(srcTag);
        newFilters.put(srcTag, ExprNodeDescUtils.backtrack(filter, op, terminal));
      }
      desc.setFilters(filters = newFilters);
    }

    // create dumpfile prefix needed to create descriptor
    String dumpFilePrefix = "";
    if (mapAliases != null) {
      for (String mapAlias : mapAliases) {
        dumpFilePrefix = dumpFilePrefix + mapAlias;
      }
      dumpFilePrefix = dumpFilePrefix + "-" + PlanUtils.getCountForMapJoinDumpFilePrefix();
    } else {
      dumpFilePrefix = "mapfile" + PlanUtils.getCountForMapJoinDumpFilePrefix();
    }

    List<ExprNodeDesc> keyCols = keyExprMap.get((byte) mapJoinPos);
    if (keyCols == null) {
      return null;
    }

    List<String> outputColumnNames = op.getConf().getOutputColumnNames();
    TableDesc keyTableDesc =
        PlanUtils.getMapJoinKeyTableDesc(hconf,
            PlanUtils.getFieldSchemasFromColumnList(keyCols, MAPJOINKEY_FIELDPREFIX));
    JoinCondDesc[] joinCondns = op.getConf().getConds();
    MapJoinDesc mapJoinDescriptor =
        new MapJoinDesc(keyExprMap, keyTableDesc, newValueExprs, valueTableDescs,
            valueFilteredTableDescs, outputColumnNames, mapJoinPos, joinCondns, filters,
            op.getConf().getNoOuterJoin(), dumpFilePrefix,
            op.getConf().getMemoryMonitorInfo(), op.getConf().getInMemoryDataSize());
    mapJoinDescriptor.setStatistics(op.getConf().getStatistics());
    mapJoinDescriptor.setTagOrder(tagOrder);
    mapJoinDescriptor.setNullSafes(desc.getNullSafes());
    mapJoinDescriptor.setFilterMap(desc.getFilterMap());
    mapJoinDescriptor.setResidualFilterExprs(desc.getResidualFilterExprs());
    mapJoinDescriptor.setColumnExprMap(colExprMap);
    if (!valueIndices.isEmpty()) {
      mapJoinDescriptor.setValueIndices(valueIndices);
    }

    return mapJoinDescriptor;
  }
@param listRejectedMapJoins
         the listRejectedMapJoins to set
ql/src/java/org/apache/hadoop/hive/ql/optimizer/MapJoinProcessor.java