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

      BucketingSortingCtx bctx = (BucketingSortingCtx)procCtx;
      JoinOperator jop = (JoinOperator)nd;
      JoinDesc joinDesc = jop.getConf();

      Byte[] order = joinDesc.getTagOrder();
      Map<Byte, List<ExprNodeDesc>> expressions = joinDesc.getExprs();
      List<String> outputValNames = joinDesc.getOutputColumnNames();

      BucketCol[] newBucketCols = null;
      SortCol[] newSortCols = null;

      for (int i = 0; i < jop.getParentOperators().size(); i++) {

        Operator<? extends OperatorDesc> parent = jop.getParentOperators().get(i);

        // The caller of this method should guarantee this
        assert(parent instanceof ReduceSinkOperator);

        ReduceSinkOperator rop = (ReduceSinkOperator)jop.getParentOperators().get(i);
        ReduceSinkDesc rsDesc = rop.getConf();

        byte tag = (byte) rsDesc.getTag();
        List<ExprNodeDesc> joinValues = expressions.get(tag);

        // Columns are output from the join from the different reduce sinks in the order of their
        // offsets
        int offset = 0;
        for (byte orderIndex = 0; orderIndex < order.length; orderIndex++) {
          if (order[orderIndex] < order[tag]) {
            offset += expressions.get(orderIndex).size();
          }
        }

        String sortOrder = rsDesc.getOrder();
        List<ExprNodeDesc> keyCols = rsDesc.getKeyCols();
        List<ExprNodeDesc> valCols = ExprNodeDescUtils.backtrack(joinValues, jop, parent);

        if (newBucketCols == null) {
          newBucketCols = new BucketCol[keyCols.size()];
          newSortCols = new SortCol[keyCols.size()];
        }

        // Go through the Reduce keys and find the matching column(s) in the reduce values
        for (int keyIndex = 0 ; keyIndex < keyCols.size(); keyIndex++) {
          ExprNodeDesc key = keyCols.get(keyIndex);
          int index = ExprNodeDescUtils.indexOf(key, valCols);
          if (index >= 0) {
            int vindex = offset + index;
            String vname = outputValNames.get(vindex);
            if (newBucketCols[keyIndex] != null) {
              newBucketCols[keyIndex].addAlias(vname, vindex);
              newSortCols[keyIndex].addAlias(vname, vindex);
            } else {
              newBucketCols[keyIndex] = new BucketCol(vname, vindex);
              newSortCols[keyIndex] = new SortCol(vname, vindex, sortOrder.charAt(keyIndex));
            }
          }
        }
      }

      List<BucketCol> bucketCols = Arrays.asList(newBucketCols);
      if (!bucketCols.contains(null)) {
        bctx.setBucketedCols(jop, bucketCols);
      }
      List<SortCol> sortCols = Arrays.asList(newSortCols);
      if (!sortCols.contains(null)) {
        bctx.setSortedCols(jop, sortCols);
      }

      return null;
    }
Processor for Join Operator.

This handles common joins, the tree should look like
ReducSinkOperator
                     \
    ....           ---  JoinOperator
                     /
ReduceSink Operator
ql/src/java/org/apache/hadoop/hive/ql/optimizer/physical/BucketingSortingOpProcFactory.java